mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Add API method that fetches the scheduled workflow steps for a resource
Closes #43660 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
6fa890fd87
commit
65ab7f541d
@ -24,4 +24,5 @@ public final class WorkflowConstants {
|
||||
// Entry configuration keys for WorkflowStep
|
||||
public static final String CONFIG_AFTER = "after";
|
||||
public static final String CONFIG_PRIORITY = "priority";
|
||||
public static final String CONFIG_SCHEDULED_AT = "scheduled-at";
|
||||
}
|
||||
|
||||
@ -8,20 +8,23 @@ import org.keycloak.common.util.MultivaluedHashMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_AFTER;
|
||||
import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_PRIORITY;
|
||||
import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_SCHEDULED_AT;
|
||||
import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_USES;
|
||||
import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_WITH;
|
||||
|
||||
@JsonPropertyOrder({CONFIG_USES, CONFIG_AFTER, CONFIG_PRIORITY, CONFIG_WITH})
|
||||
@JsonPropertyOrder({CONFIG_USES, CONFIG_AFTER, CONFIG_PRIORITY, CONFIG_WITH, CONFIG_SCHEDULED_AT})
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public final class WorkflowStepRepresentation extends AbstractWorkflowComponentRepresentation {
|
||||
public class WorkflowStepRepresentation extends AbstractWorkflowComponentRepresentation {
|
||||
|
||||
private final String uses;
|
||||
private Long scheduledAt;
|
||||
|
||||
public static Builder create() {
|
||||
return new Builder();
|
||||
@ -36,8 +39,13 @@ public final class WorkflowStepRepresentation extends AbstractWorkflowComponentR
|
||||
}
|
||||
|
||||
public WorkflowStepRepresentation(String id, String uses, MultivaluedHashMap<String, String> config) {
|
||||
this(id, uses, config, null);
|
||||
}
|
||||
|
||||
public WorkflowStepRepresentation(String id, String uses, MultivaluedHashMap<String, String> config, Long scheduledAt) {
|
||||
super(id, config);
|
||||
this.uses = uses;
|
||||
this.scheduledAt = scheduledAt;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@ -72,6 +80,15 @@ public final class WorkflowStepRepresentation extends AbstractWorkflowComponentR
|
||||
setConfig(CONFIG_PRIORITY, String.valueOf(ms));
|
||||
}
|
||||
|
||||
@JsonProperty(CONFIG_SCHEDULED_AT)
|
||||
public Long getScheduledAt() {
|
||||
return this.scheduledAt;
|
||||
}
|
||||
|
||||
public void setScheduledAt(Long scheduledAt) {
|
||||
this.scheduledAt = scheduledAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
|
||||
@ -41,6 +41,11 @@ public interface WorkflowsResource {
|
||||
@QueryParam("max") Integer maxResults
|
||||
);
|
||||
|
||||
@Path("scheduled/{resource-id}")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<WorkflowRepresentation> getScheduledWorkflows(@PathParam("resource-id") String resourceId);
|
||||
|
||||
@Path("{id}")
|
||||
WorkflowResource workflow(@PathParam("id") String id);
|
||||
}
|
||||
|
||||
@ -116,6 +116,25 @@ public class DefaultWorkflowProvider implements WorkflowProvider {
|
||||
.map(c -> new Workflow(session, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<WorkflowRepresentation> getScheduledWorkflowsByResource(String resourceId) {
|
||||
return stateProvider.getScheduledStepsByResource(resourceId).map(scheduledStep -> {
|
||||
Workflow workflow = getWorkflow(scheduledStep.workflowId());
|
||||
// get the steps starting from the scheduled step, then add their scheduledAt
|
||||
List<WorkflowStepRepresentation> steps = workflow.getSteps(scheduledStep.stepId()).map(this::toRepresentation).toList();
|
||||
Long scheduledAt = null;
|
||||
for (WorkflowStepRepresentation step : steps) {
|
||||
if (scheduledAt == null) {
|
||||
scheduledAt = scheduledStep.scheduledAt();
|
||||
} else if (step.getAfter() != null) {
|
||||
scheduledAt += DurationConverter.parseDuration(step.getAfter()).toMillis();
|
||||
}
|
||||
step.setScheduledAt(scheduledAt);
|
||||
}
|
||||
return new WorkflowRepresentation(workflow.getId(), workflow.getName(), workflow.getConfig(), steps);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(WorkflowEvent event) {
|
||||
processEvent(getWorkflows(), event);
|
||||
@ -128,7 +147,7 @@ public class DefaultWorkflowProvider implements WorkflowProvider {
|
||||
log.debugf("Skipping workflow %s as it is disabled", workflow.getName());
|
||||
return;
|
||||
}
|
||||
for (ScheduledStep scheduled : stateProvider.getDueScheduledSteps(workflow)) {
|
||||
stateProvider.getDueScheduledSteps(workflow).forEach((scheduled) -> {
|
||||
// check if the resource is still passes the workflow's resource conditions
|
||||
DefaultWorkflowExecutionContext context = new DefaultWorkflowExecutionContext(session, workflow, scheduled);
|
||||
EventBasedWorkflow provider = new EventBasedWorkflow(session, getWorkflowComponent(workflow.getId()));
|
||||
@ -146,7 +165,7 @@ public class DefaultWorkflowProvider implements WorkflowProvider {
|
||||
runWorkflow(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -229,7 +248,7 @@ public class DefaultWorkflowProvider implements WorkflowProvider {
|
||||
|
||||
private void processEvent(Stream<Workflow> workflows, WorkflowEvent event) {
|
||||
Map<String, ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByResource(event.getResourceId())
|
||||
.stream().collect(Collectors.toMap(ScheduledStep::workflowId, Function.identity()));
|
||||
.collect(Collectors.toMap(ScheduledStep::workflowId, Function.identity()));
|
||||
|
||||
workflows.forEach(workflow -> {
|
||||
if (!workflow.isEnabled()) {
|
||||
|
||||
@ -19,7 +19,7 @@ package org.keycloak.models.workflow;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
@ -85,7 +85,7 @@ public class JpaWorkflowStateProvider implements WorkflowStateProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ScheduledStep> getDueScheduledSteps(Workflow workflow) {
|
||||
public Stream<ScheduledStep> getDueScheduledSteps(Workflow workflow) {
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<WorkflowStateEntity> query = cb.createQuery(WorkflowStateEntity.class);
|
||||
Root<WorkflowStateEntity> stateRoot = query.from(WorkflowStateEntity.class);
|
||||
@ -96,14 +96,13 @@ public class JpaWorkflowStateProvider implements WorkflowStateProvider {
|
||||
query.where(cb.and(byWorkflow, isExpired));
|
||||
|
||||
return em.createQuery(query).getResultStream()
|
||||
.map(this::toScheduledStep)
|
||||
.toList();
|
||||
.map(this::toScheduledStep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ScheduledStep> getScheduledStepsByWorkflow(String workflowId) {
|
||||
public Stream<ScheduledStep> getScheduledStepsByWorkflow(String workflowId) {
|
||||
if (StringUtil.isBlank(workflowId)) {
|
||||
return List.of();
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
@ -114,12 +113,11 @@ public class JpaWorkflowStateProvider implements WorkflowStateProvider {
|
||||
query.where(byWorkflow);
|
||||
|
||||
return em.createQuery(query).getResultStream()
|
||||
.map(this::toScheduledStep)
|
||||
.toList();
|
||||
.map(this::toScheduledStep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ScheduledStep> getScheduledStepsByResource(String resourceId) {
|
||||
public Stream<ScheduledStep> getScheduledStepsByResource(String resourceId) {
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<WorkflowStateEntity> query = cb.createQuery(WorkflowStateEntity.class);
|
||||
Root<WorkflowStateEntity> stateRoot = query.from(WorkflowStateEntity.class);
|
||||
@ -128,8 +126,7 @@ public class JpaWorkflowStateProvider implements WorkflowStateProvider {
|
||||
query.where(byResource);
|
||||
|
||||
return em.createQuery(query).getResultStream()
|
||||
.map(this::toScheduledStep)
|
||||
.toList();
|
||||
.map(this::toScheduledStep);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -221,6 +218,7 @@ public class JpaWorkflowStateProvider implements WorkflowStateProvider {
|
||||
}
|
||||
|
||||
private ScheduledStep toScheduledStep(WorkflowStateEntity entity) {
|
||||
return new ScheduledStep(entity.getWorkflowId(), entity.getScheduledStepId(), entity.getResourceId(), entity.getExecutionId());
|
||||
return new ScheduledStep(entity.getWorkflowId(), entity.getScheduledStepId(), entity.getResourceId(),
|
||||
entity.getExecutionId(), entity.getScheduledStepTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +119,26 @@ public class Workflow {
|
||||
.map(WorkflowStep::new).sorted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get steps starting from the specified stepId (inclusive)
|
||||
*
|
||||
* @param stepId the step id to start from
|
||||
* @return the stream of workflow steps
|
||||
*/
|
||||
public Stream<WorkflowStep> getSteps(String stepId) {
|
||||
boolean[] startAdding = {stepId == null};
|
||||
return getSteps().filter(step -> {
|
||||
if (startAdding[0]) {
|
||||
return true;
|
||||
}
|
||||
if (step.getId().equals(stepId)) {
|
||||
startAdding[0] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public WorkflowStep getStepById(String id) {
|
||||
return getSteps().filter(s -> s.getId().equals(id)).findAny().orElse(null);
|
||||
}
|
||||
|
||||
@ -53,6 +53,8 @@ public interface WorkflowProvider extends Provider {
|
||||
.skip(first).limit(max);
|
||||
}
|
||||
|
||||
Stream<WorkflowRepresentation> getScheduledWorkflowsByResource(String resourceId);
|
||||
|
||||
WorkflowRepresentation toRepresentation(Workflow workflow);
|
||||
|
||||
void updateWorkflow(Workflow workflow, WorkflowRepresentation rep);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
package org.keycloak.models.workflow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
@ -69,19 +69,19 @@ public interface WorkflowStateProvider extends Provider {
|
||||
|
||||
ScheduledStep getScheduledStep(String workflowId, String resourceId);
|
||||
|
||||
List<ScheduledStep> getScheduledStepsByResource(String resourceId);
|
||||
Stream<ScheduledStep> getScheduledStepsByResource(String resourceId);
|
||||
|
||||
List<ScheduledStep> getScheduledStepsByWorkflow(String workflowId);
|
||||
Stream<ScheduledStep> getScheduledStepsByWorkflow(String workflowId);
|
||||
|
||||
default List<ScheduledStep> getScheduledStepsByWorkflow(Workflow workflow) {
|
||||
default Stream<ScheduledStep> getScheduledStepsByWorkflow(Workflow workflow) {
|
||||
if (workflow == null) {
|
||||
return List.of();
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return getScheduledStepsByWorkflow(workflow.getId());
|
||||
}
|
||||
|
||||
List<ScheduledStep> getDueScheduledSteps(Workflow workflow);
|
||||
Stream<ScheduledStep> getDueScheduledSteps(Workflow workflow);
|
||||
|
||||
record ScheduledStep(String workflowId, String stepId, String resourceId, String executionId) {}
|
||||
record ScheduledStep(String workflowId, String stepId, String resourceId, String executionId, long scheduledAt) {}
|
||||
}
|
||||
|
||||
@ -83,4 +83,14 @@ public class WorkflowsResource {
|
||||
int max = Optional.ofNullable(maxResults).orElse(10);
|
||||
return provider.getWorkflows(search, exact, first, max).map(provider::toRepresentation).toList();
|
||||
}
|
||||
|
||||
@Path("scheduled/{resource-id}")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<WorkflowRepresentation> getScheduledSteps(
|
||||
@PathParam("resource-id") String resourceId
|
||||
) {
|
||||
auth.realm().requireManageRealm();
|
||||
return provider.getScheduledWorkflowsByResource(resourceId).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.workflow.NotifyUserStepProviderFactory;
|
||||
import org.keycloak.models.workflow.ResourceType;
|
||||
import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider;
|
||||
import org.keycloak.models.workflow.WorkflowProvider;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.workflows.WorkflowRepresentation;
|
||||
@ -244,10 +244,10 @@ public class AdhocWorkflowTest extends AbstractWorkflowTest {
|
||||
assertThat(user.getAttributes().keySet(), hasItems("workflowOne", "workflowTwo"));
|
||||
|
||||
// Verify that the steps are scheduled for the user
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByResource(user.getId());
|
||||
assertNotNull(scheduledSteps, "Two steps should have been scheduled for the user " + user.getUsername());
|
||||
assertThat(scheduledSteps, hasSize(2));
|
||||
WorkflowProvider provider = session.getProvider(WorkflowProvider.class);
|
||||
List<WorkflowRepresentation> scheduledWorkflows = provider.getScheduledWorkflowsByResource(user.getId()).toList();
|
||||
assertNotNull(scheduledWorkflows, "Two workflow steps should have been scheduled for the user " + user.getUsername());
|
||||
assertThat(scheduledWorkflows, hasSize(2));
|
||||
});
|
||||
|
||||
//deactivate workflow One
|
||||
@ -255,9 +255,9 @@ public class AdhocWorkflowTest extends AbstractWorkflowTest {
|
||||
|
||||
runOnServer.run(session -> {
|
||||
// Verify that there is single step scheduled for the user
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByResource(id);
|
||||
assertThat(scheduledSteps, hasSize(1));
|
||||
WorkflowProvider provider = session.getProvider(WorkflowProvider.class);
|
||||
List<WorkflowRepresentation> scheduledWorkflows = provider.getScheduledWorkflowsByResource(id).toList();
|
||||
assertThat(scheduledWorkflows, hasSize(1));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ import org.keycloak.models.workflow.ResourceOperationType;
|
||||
import org.keycloak.models.workflow.Workflow;
|
||||
import org.keycloak.models.workflow.WorkflowProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider.ScheduledStep;
|
||||
import org.keycloak.models.workflow.conditions.IdentityProviderWorkflowConditionFactory;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
@ -253,7 +254,7 @@ public class BrokeredUserSessionRefreshTimeWorkflowTest extends AbstractWorkflow
|
||||
|
||||
// alice should be associated with the workflow
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
WorkflowStateProvider.ScheduledStep scheduledStep = stateProvider.getScheduledStep(workflow.getId(), alice.getId());
|
||||
ScheduledStep scheduledStep = stateProvider.getScheduledStep(workflow.getId(), alice.getId());
|
||||
assertNotNull(scheduledStep, "A step should have been scheduled for the user " + alice.getUsername());
|
||||
});
|
||||
|
||||
|
||||
@ -209,7 +209,7 @@ public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest {
|
||||
assertEquals(2, registeredWorkflows.size());
|
||||
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<WorkflowStateProvider.ScheduledStep> steps = stateProvider.getScheduledStepsByResource(userId);
|
||||
List<WorkflowStateProvider.ScheduledStep> steps = stateProvider.getScheduledStepsByResource(userId).toList();
|
||||
assertThat(steps, hasSize(2));
|
||||
});
|
||||
|
||||
@ -237,7 +237,7 @@ public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest {
|
||||
}
|
||||
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<WorkflowStateProvider.ScheduledStep> steps = stateProvider.getScheduledStepsByResource(userId);
|
||||
List<WorkflowStateProvider.ScheduledStep> steps = stateProvider.getScheduledStepsByResource(userId).toList();
|
||||
assertThat(steps, hasSize(0));
|
||||
});
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ public class GroupMembershipJoinWorkflowTest extends AbstractWorkflowTest {
|
||||
Workflow workflow = registeredWorkflows.get(0);
|
||||
// check workflow was correctly assigned to the users
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertThat(scheduledSteps, hasSize(10));
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
||||
import org.keycloak.models.workflow.Workflow;
|
||||
import org.keycloak.models.workflow.WorkflowProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider.ScheduledStep;
|
||||
import org.keycloak.models.workflow.conditions.RoleWorkflowConditionFactory;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
@ -112,7 +113,7 @@ public class RoleWorkflowConditionTest extends AbstractWorkflowTest {
|
||||
Workflow workflow = registeredWorkflows.get(0);
|
||||
// check workflow was correctly assigned to the users
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertThat(scheduledSteps, hasSize(10));
|
||||
});
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
||||
import org.keycloak.models.workflow.Workflow;
|
||||
import org.keycloak.models.workflow.WorkflowProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider;
|
||||
import org.keycloak.models.workflow.WorkflowStateProvider.ScheduledStep;
|
||||
import org.keycloak.models.workflow.conditions.UserAttributeWorkflowConditionFactory;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
||||
@ -106,7 +107,7 @@ public class UserAttributeWorkflowConditionTest extends AbstractWorkflowTest {
|
||||
Workflow workflow = registeredWorkflows.get(0);
|
||||
// check workflow was correctly assigned to the users
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertThat(scheduledSteps, hasSize(10));
|
||||
});
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ public class UserSessionRefreshTimeWorkflowTest extends AbstractWorkflowTest {
|
||||
// store the first step id for later comparison
|
||||
String firstStepId = runOnServer.fetch(session-> {
|
||||
WorkflowStateProvider provider = session.getProvider(WorkflowStateProvider.class);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId).toList();
|
||||
assertThat(steps, hasSize(1));
|
||||
return steps.get(0).stepId();
|
||||
}, String.class);
|
||||
@ -112,7 +112,7 @@ public class UserSessionRefreshTimeWorkflowTest extends AbstractWorkflowTest {
|
||||
assertTrue(user.isEnabled());
|
||||
|
||||
WorkflowStateProvider provider = session.getProvider(WorkflowStateProvider.class);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId).toList();
|
||||
assertThat(steps, hasSize(1));
|
||||
return steps.get(0).stepId();
|
||||
}, String.class);
|
||||
@ -128,7 +128,7 @@ public class UserSessionRefreshTimeWorkflowTest extends AbstractWorkflowTest {
|
||||
|
||||
runOnServer.run(session -> {
|
||||
WorkflowStateProvider provider = session.getProvider(WorkflowStateProvider.class);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId).toList();
|
||||
// step id must remain the same as before
|
||||
assertThat(steps, hasSize(1));
|
||||
assertThat(steps.get(0).stepId(), is(secondStepId));
|
||||
@ -140,7 +140,7 @@ public class UserSessionRefreshTimeWorkflowTest extends AbstractWorkflowTest {
|
||||
// workflow should be restarted and the first step should be scheduled again
|
||||
runOnServer.run(session -> {
|
||||
WorkflowStateProvider provider = session.getProvider(WorkflowStateProvider.class);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId);
|
||||
List< WorkflowStateProvider.ScheduledStep> steps = provider.getScheduledStepsByResource(userId).toList();
|
||||
// step id must be the first one now as the workflow was restarted
|
||||
assertThat(steps, hasSize(1));
|
||||
assertThat(steps.get(0).stepId(), is(firstStepId));
|
||||
|
||||
@ -85,6 +85,7 @@ import static org.keycloak.models.workflow.ResourceOperationType.USER_ADDED;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
@ -221,7 +222,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
List<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||
assertEquals(1, registeredWorkflows.size());
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<ScheduledStep> steps = stateProvider.getScheduledStepsByWorkflow(workflowId);
|
||||
List<ScheduledStep> steps = stateProvider.getScheduledStepsByWorkflow(workflowId).toList();
|
||||
assertTrue(steps.isEmpty());
|
||||
});
|
||||
}
|
||||
@ -364,14 +365,8 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
managedRealm.admin().workflows().workflow(workflowId).activate(ResourceType.USERS.name(), userAlice.getId());
|
||||
|
||||
// check step has been scheduled for the user
|
||||
runOnServer.run((RunOnServer) session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserByUsername(realm, "alice");
|
||||
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByResource(user.getId());
|
||||
assertThat("A step should have been scheduled for the user " + user.getUsername(), scheduledSteps, hasSize(1));
|
||||
});
|
||||
List<WorkflowRepresentation> scheduledSteps = managedRealm.admin().workflows().getScheduledWorkflows(userAlice.getId());
|
||||
assertThat("A step should have been scheduled for the user " + userAlice.getUsername(), scheduledSteps, hasSize(1));
|
||||
|
||||
// now update the workflow to add a condition that will make the user no longer eligible
|
||||
WorkflowRepresentation workflow = managedRealm.admin().workflows().workflow(workflowId).toRepresentation();
|
||||
@ -382,15 +377,8 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
runScheduledSteps(Duration.ofDays(6));
|
||||
|
||||
// check the user is still enabled and no scheduled steps exist
|
||||
runOnServer.run((RunOnServer) session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserByUsername(realm, "alice");
|
||||
assertThat(user.isEnabled(), is(true));
|
||||
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByResource(user.getId());
|
||||
assertThat(scheduledSteps, empty());
|
||||
});
|
||||
scheduledSteps = managedRealm.admin().workflows().getScheduledWorkflows(userAlice.getId());
|
||||
assertThat(scheduledSteps, empty());
|
||||
|
||||
}
|
||||
|
||||
@ -434,6 +422,54 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
assertThat(representations.get(0).getName(), is("gamma-workflow"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetActiveWorkflowsForResource() {
|
||||
// create a few of simple workflows
|
||||
String[] workflowNames = {"workflow-one", "workflow-two", "workflow-three", "workflow-four"};
|
||||
for (String name : workflowNames) {
|
||||
|
||||
String workflowId;
|
||||
try (Response response =
|
||||
managedRealm.admin().workflows().create(WorkflowRepresentation.withName(name)
|
||||
.withSteps(
|
||||
WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID)
|
||||
.after(Duration.ofDays(5))
|
||||
.build(),
|
||||
WorkflowStepRepresentation.create().of(SetUserAttributeStepProviderFactory.ID)
|
||||
.withConfig("key", "value")
|
||||
.build(),
|
||||
WorkflowStepRepresentation.create().of(DisableUserStepProviderFactory.ID)
|
||||
.after(Duration.ofDays(15))
|
||||
.build()
|
||||
).build())) {
|
||||
workflowId = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
// bind all workflows, except the second one, to user alice
|
||||
if (!name.equals("workflow-two")) {
|
||||
managedRealm.admin().workflows().workflow(workflowId).activate(ResourceType.USERS.name(), userAlice.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// use the API to fetch the active workflows for the user
|
||||
List<WorkflowRepresentation> scheduledWorkflows = managedRealm.admin().workflows().getScheduledWorkflows(userAlice.getId());
|
||||
assertThat(scheduledWorkflows, hasSize(3));
|
||||
// assert that "workflow-two" is not among them
|
||||
assertTrue(scheduledWorkflows.stream().noneMatch(step -> step.getName().equals("workflow-two")));
|
||||
|
||||
// assert that all workflows have the scheduledAt set to a positive value, and that the first and second steps are scheduled for the same time
|
||||
for (WorkflowRepresentation workflow : scheduledWorkflows) {
|
||||
assertThat(workflow.getSteps(), hasSize(3));
|
||||
assertThat(workflow.getSteps().get(0).getScheduledAt(), greaterThan(0L));
|
||||
assertThat(workflow.getSteps().get(1).getScheduledAt(), greaterThan(0L));
|
||||
assertThat(workflow.getSteps().get(0).getScheduledAt(), equalTo(workflow.getSteps().get(1).getScheduledAt()));
|
||||
// the third step have a scheduledAt higher than the previous two
|
||||
assertThat(workflow.getSteps().get(2).getScheduledAt(), greaterThan(workflow.getSteps().get(1).getScheduledAt()));
|
||||
// it should be precisely 15 days after the second step
|
||||
long expectedThirdStepScheduledAt = workflow.getSteps().get(1).getScheduledAt() + Duration.ofDays(15).toMillis();
|
||||
assertThat(workflow.getSteps().get(2).getScheduledAt(), is(expectedThirdStepScheduledAt));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkflowDoesNotFallThroughStepsInSingleRun() {
|
||||
@ -558,7 +594,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
|
||||
// check no workflows are yet attached to the previous users, only to the ones created after the workflow was in place
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertEquals(3, scheduledSteps.size());
|
||||
scheduledSteps.forEach(scheduledStep -> {
|
||||
assertEquals(notifyStep.getId(), scheduledStep.stepId());
|
||||
@ -581,7 +617,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
Workflow workflow = registeredWorkflows.get(0);
|
||||
WorkflowStep disableStep = workflow.getSteps().toList().get(1);
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertEquals(3, scheduledSteps.size());
|
||||
scheduledSteps.forEach(scheduledStep -> {
|
||||
assertEquals(disableStep.getId(), scheduledStep.stepId());
|
||||
@ -608,7 +644,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
Workflow workflow = registeredWorkflows.get(0);
|
||||
// check workflow was correctly assigned to the old users, not affecting users already associated with the workflow.
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow).toList();
|
||||
assertEquals(13, scheduledSteps.size());
|
||||
|
||||
List<WorkflowStep> steps = workflow.getSteps().toList();
|
||||
@ -693,7 +729,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
List<Workflow> registeredWorkflow = provider.getWorkflows().toList();
|
||||
assertEquals(1, registeredWorkflow.size());
|
||||
WorkflowStateProvider stateProvider = session.getKeycloakSessionFactory().getProviderFactory(WorkflowStateProvider.class).create(session);
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(registeredWorkflow.get(0));
|
||||
List<ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(registeredWorkflow.get(0)).toList();
|
||||
|
||||
// verify that there's only one scheduled step, for the first user
|
||||
assertEquals(1, scheduledSteps.size());
|
||||
@ -915,18 +951,17 @@ public class WorkflowManagementTest extends AbstractWorkflowTest {
|
||||
.build()
|
||||
).build()).close();
|
||||
|
||||
managedRealm.admin().users().create(UserConfigBuilder.create().username("testuser4").name("NoEmail", "").build()).close();
|
||||
String userId;
|
||||
try (Response response = managedRealm.admin().users().create(UserConfigBuilder.create().username("testuser4").name("NoEmail", "").build())) {
|
||||
userId = ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
runScheduledSteps(Duration.ofDays(5));
|
||||
|
||||
runOnServer.run(session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
// But should still create state record for the workflow flow
|
||||
UserModel user = session.users().getUserByUsername(realm, "testuser4");
|
||||
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||
var scheduledSteps = stateProvider.getScheduledStepsByResource(user.getId());
|
||||
assertEquals(1, scheduledSteps.size());
|
||||
});
|
||||
List<WorkflowRepresentation> scheduledWorkflows = managedRealm.admin().workflows().getScheduledWorkflows(userId);
|
||||
assertThat(scheduledWorkflows, hasSize(1));
|
||||
List<WorkflowStepRepresentation> steps = scheduledWorkflows.get(0).getSteps();
|
||||
assertThat(steps, hasSize(1));
|
||||
assertEquals(DisableUserStepProviderFactory.ID, steps.get(0).getUses());
|
||||
|
||||
// Should NOT send email to user without email address
|
||||
MimeMessage testUserMessage = findEmailByRecipientContaining("testuser4");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user