diff --git a/core/src/main/java/org/keycloak/representations/workflows/WorkflowConstants.java b/core/src/main/java/org/keycloak/representations/workflows/WorkflowConstants.java index 7a92a01e4b8..02528385eb4 100644 --- a/core/src/main/java/org/keycloak/representations/workflows/WorkflowConstants.java +++ b/core/src/main/java/org/keycloak/representations/workflows/WorkflowConstants.java @@ -2,8 +2,7 @@ package org.keycloak.representations.workflows; public final class WorkflowConstants { - public static final String DEFAULT_WORKFLOW = "event-based-workflow"; - + public static final String AD_HOC = "adhoc"; public static final String CONFIG_USES = "uses"; public static final String CONFIG_WITH = "with"; public static final String CONFIG_SUPPORTS = "supports"; diff --git a/core/src/main/java/org/keycloak/representations/workflows/WorkflowRepresentation.java b/core/src/main/java/org/keycloak/representations/workflows/WorkflowRepresentation.java index 5e340766db0..0801bffda8b 100644 --- a/core/src/main/java/org/keycloak/representations/workflows/WorkflowRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/workflows/WorkflowRepresentation.java @@ -203,7 +203,7 @@ public final class WorkflowRepresentation extends AbstractWorkflowComponentRepre } public Builder onEvent(String... operation) { - return onEvent(String.join(" or ", operation).toUpperCase()); + return onEvent(String.join(" or ", operation)); } public Builder onCondition(String condition) { diff --git a/model/jpa/src/main/antlr4/org/keycloak/models/workflow/conditions/expression/BooleanConditionLexer.g4 b/model/jpa/src/main/antlr4/org/keycloak/models/workflow/expression/BooleanConditionLexer.g4 similarity index 100% rename from model/jpa/src/main/antlr4/org/keycloak/models/workflow/conditions/expression/BooleanConditionLexer.g4 rename to model/jpa/src/main/antlr4/org/keycloak/models/workflow/expression/BooleanConditionLexer.g4 diff --git a/model/jpa/src/main/antlr4/org/keycloak/models/workflow/conditions/expression/BooleanConditionParser.g4 b/model/jpa/src/main/antlr4/org/keycloak/models/workflow/expression/BooleanConditionParser.g4 similarity index 100% rename from model/jpa/src/main/antlr4/org/keycloak/models/workflow/conditions/expression/BooleanConditionParser.g4 rename to model/jpa/src/main/antlr4/org/keycloak/models/workflow/expression/BooleanConditionParser.g4 diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index b8a9d6f8650..88ec781c387 100644 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -900,7 +900,17 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc resource = toClientModel(realm, entity); - session.getKeycloakSessionFactory().publish((ClientModel.ClientCreationEvent) () -> resource); + session.getKeycloakSessionFactory().publish(new ClientModel.ClientCreationEvent() { + @Override + public ClientModel getCreatedClient() { + return resource; + } + + @Override + public KeycloakSession getKeycloakSession() { + return session; + } + }); return resource; } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/ClientResourceTypeWorkflowProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/ClientResourceTypeWorkflowProvider.java index 2a7feb5e359..9f3ff588419 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/ClientResourceTypeWorkflowProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/ClientResourceTypeWorkflowProvider.java @@ -16,9 +16,9 @@ import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.jpa.entities.ClientEntity; -import org.keycloak.models.workflow.conditions.expression.BooleanConditionParser; -import org.keycloak.models.workflow.conditions.expression.EvaluatorUtils; -import org.keycloak.models.workflow.conditions.expression.PredicateEvaluator; +import org.keycloak.models.workflow.expression.BooleanConditionParser; +import org.keycloak.models.workflow.expression.EvaluatorUtils; +import org.keycloak.models.workflow.expression.PredicateEvaluator; import org.keycloak.representations.workflows.WorkflowConstants; import org.keycloak.utils.StringUtil; diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/EventBasedWorkflow.java b/model/jpa/src/main/java/org/keycloak/models/workflow/EventBasedWorkflow.java index bff40f02a8b..36c96b8972b 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/EventBasedWorkflow.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/EventBasedWorkflow.java @@ -2,10 +2,11 @@ package org.keycloak.models.workflow; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.workflow.conditions.expression.BooleanConditionParser; -import org.keycloak.models.workflow.conditions.expression.ConditionEvaluator; -import org.keycloak.models.workflow.conditions.expression.EvaluatorUtils; -import org.keycloak.models.workflow.conditions.expression.EventEvaluator; +import org.keycloak.models.workflow.expression.BooleanConditionParser; +import org.keycloak.models.workflow.expression.ConditionEvaluator; +import org.keycloak.models.workflow.expression.EvaluatorUtils; +import org.keycloak.models.workflow.expression.EventEvaluator; +import org.keycloak.representations.workflows.WorkflowConstants; import org.keycloak.utils.StringUtil; import static org.keycloak.representations.workflows.WorkflowConstants.CONFIG_CANCEL_IN_PROGRESS; @@ -41,7 +42,7 @@ final class EventBasedWorkflow { if (event == null) { return false; } - return supports(event.getResourceType()) && activateOnEvent(event) && validateResourceConditions(executionContext); + return supports(event.getResourceType()) && activateOnEvent(executionContext) && validateResourceConditions(executionContext); } /** @@ -89,19 +90,19 @@ final class EventBasedWorkflow { /** * Determines whether the workflow should be activated based on the given event or not. * - * @param event a reference to the workflow event. + * @param executionContext a reference to the workflow execution context. * @return {@code true} if the workflow should be activated, {@code false} otherwise. */ - private boolean activateOnEvent(WorkflowEvent event) { + private boolean activateOnEvent(WorkflowExecutionContext executionContext) { // AD_HOC is a special case that always triggers the workflow regardless of the configured activation events - if (ResourceOperationType.AD_HOC.equals(event.getOperation())) { + if (WorkflowConstants.AD_HOC.equals(executionContext.getEvent().getEventProviderId())) { return true; } String eventConditions = model.getConfig().getFirst(CONFIG_ON_EVENT); if (StringUtil.isNotBlank(eventConditions)) { BooleanConditionParser.EvaluatorContext context = EvaluatorUtils.createEvaluatorContext(model, eventConditions); - EventEvaluator eventEvaluator = new EventEvaluator(event); + EventEvaluator eventEvaluator = new EventEvaluator(session, executionContext); return eventEvaluator.visit(context); } else { return false; @@ -131,7 +132,7 @@ final class EventBasedWorkflow { else { // the flag has an event expression - parse and evaluate it BooleanConditionParser.EvaluatorContext context = EvaluatorUtils.createEvaluatorContext(model, concurrencySetting); - EventEvaluator eventEvaluator = new EventEvaluator(executionContext.getEvent()); + EventEvaluator eventEvaluator = new EventEvaluator(session, executionContext); return eventEvaluator.visit(context); } } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/RestartWorkflowStepProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/RestartWorkflowStepProviderFactory.java index fd1eddfbf08..f32dee5e59e 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/RestartWorkflowStepProviderFactory.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/RestartWorkflowStepProviderFactory.java @@ -30,7 +30,7 @@ public final class RestartWorkflowStepProviderFactory implements WorkflowStepPro } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { // Usable for all resource types. return Set.of(ResourceType.values()); } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/ScheduleWorkflowTask.java b/model/jpa/src/main/java/org/keycloak/models/workflow/ScheduleWorkflowTask.java index 2629b7d3746..739ae59c039 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/ScheduleWorkflowTask.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/ScheduleWorkflowTask.java @@ -31,7 +31,7 @@ final class ScheduleWorkflowTask extends WorkflowTransactionalTask { WorkflowEvent event = workflowContext.getEvent(); WorkflowStep firstStep = workflow.getSteps().findFirst().orElseThrow(() -> new WorkflowInvalidStateException("No steps found for workflow " + workflow.getName())); log.debugf("Scheduling first step '%s' of workflow '%s' for resource %s based on on event %s with notBefore %d", - firstStep.getProviderId(), workflow.getName(), event.getResourceId(), event.getOperation(), workflow.getNotBefore()); + firstStep.getProviderId(), workflow.getName(), event.getResourceId(), event.getEventProviderId(), workflow.getNotBefore()); String originalAfter = firstStep.getAfter(); try { firstStep.setAfter(workflow.getNotBefore()); @@ -46,7 +46,7 @@ final class ScheduleWorkflowTask extends WorkflowTransactionalTask { @Override public String toString() { WorkflowEvent event = context.getEvent(); - return "eventType=" + event.getOperation() + + return "eventType=" + event.getEventProviderId() + ",resourceType=" + event.getResourceType() + ",resourceId=" + event.getResourceId(); } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/UserResourceTypeWorkflowProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/UserResourceTypeWorkflowProvider.java index dfe53b8fc67..a34691ce910 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/UserResourceTypeWorkflowProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/UserResourceTypeWorkflowProvider.java @@ -33,9 +33,9 @@ import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.jpa.entities.UserEntity; -import org.keycloak.models.workflow.conditions.expression.BooleanConditionParser; -import org.keycloak.models.workflow.conditions.expression.EvaluatorUtils; -import org.keycloak.models.workflow.conditions.expression.PredicateEvaluator; +import org.keycloak.models.workflow.expression.BooleanConditionParser; +import org.keycloak.models.workflow.expression.EvaluatorUtils; +import org.keycloak.models.workflow.expression.PredicateEvaluator; import org.keycloak.representations.workflows.WorkflowConstants; import org.keycloak.utils.StringUtil; diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/WorkflowValidator.java b/model/jpa/src/main/java/org/keycloak/models/workflow/WorkflowValidator.java index d90ed7463fe..713c15904d3 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/WorkflowValidator.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/WorkflowValidator.java @@ -9,10 +9,10 @@ import java.util.stream.Collectors; import org.keycloak.common.util.DurationConverter; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.workflow.conditions.expression.BooleanConditionParser; -import org.keycloak.models.workflow.conditions.expression.ConditionNameCollector; -import org.keycloak.models.workflow.conditions.expression.ConditionTypeCollector; -import org.keycloak.models.workflow.conditions.expression.EvaluatorUtils; +import org.keycloak.models.workflow.expression.BooleanConditionParser; +import org.keycloak.models.workflow.expression.ConditionNameCollector; +import org.keycloak.models.workflow.expression.ConditionTypeCollector; +import org.keycloak.models.workflow.expression.EvaluatorUtils; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.utils.StringUtil; @@ -93,9 +93,9 @@ public class WorkflowValidator { // ConditionTypeCollector.visit(ctx) throws a WorkflowInvalidStateException if a provider is not found typeCollector.visit(context); - Set supporteds = typeCollector.getConditionTypes(); - if (!supporteds.contains(workflowType)) { - String formatted = supporteds.stream().map(Enum::name).collect(Collectors.joining(", ")); + Set supportedTypes = typeCollector.getConditionTypes(); + if (!supportedTypes.contains(workflowType)) { + String formatted = supportedTypes.stream().map(Enum::name).collect(Collectors.joining(", ")); throw new WorkflowInvalidStateException("Provided condition types (%s) are not compatible with workflow type (%s).".formatted(formatted, workflowType)); } } @@ -135,19 +135,12 @@ public class WorkflowValidator { ConditionNameCollector collector = new ConditionNameCollector(); collector.visit(context); - // check if there are providers for the conditions used in the expression + // check if the providers referenced in the condition and event expressions are valid if ("on".equals(fieldName) || "restart-in-progress".equals(fieldName) || "cancel-in-progress".equals(fieldName)) { - // check if we can get a ResourceOperationType for the events in the expression - for (String name : collector.getConditionNames()) { - try { - ResourceOperationType.valueOf(name.replace("-", "_").toUpperCase()); - } catch (IllegalArgumentException iae) { - throw new WorkflowInvalidStateException("Could not find event: " + name); - } - } + collector.getConditionNames().forEach(name -> Workflows.getEventProviderFactory(session, name.replace("_", "-").toLowerCase())); } else if ("if".equals(fieldName)) { - // try to get an instance of the provider -> method throws a WorkflowInvalidStateException if provider is not found - collector.getConditionNames().forEach(name -> Workflows.getConditionProvider(session, name, expression)); + // try to get an instance of the provider factory -> method throws a WorkflowInvalidStateException if provider factory is not found + collector.getConditionNames().forEach(name -> Workflows.getConditionProviderFactory(session, name.replace("_", "-").toLowerCase())); } } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/GroupMembershipWorkflowConditionProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/GroupMembershipWorkflowConditionProvider.java index bc63179863c..b013a9a7788 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/GroupMembershipWorkflowConditionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/GroupMembershipWorkflowConditionProvider.java @@ -1,7 +1,5 @@ package org.keycloak.models.workflow.conditions; -import java.util.Set; - import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; @@ -31,8 +29,8 @@ public class GroupMembershipWorkflowConditionProvider implements WorkflowConditi } @Override - public Set supportedTypes() { - return Set.of(ResourceType.USERS); + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/IdentityProviderWorkflowConditionProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/IdentityProviderWorkflowConditionProvider.java index 0e7cda3889b..03db17c7002 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/IdentityProviderWorkflowConditionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/IdentityProviderWorkflowConditionProvider.java @@ -1,6 +1,5 @@ package org.keycloak.models.workflow.conditions; -import java.util.Set; import java.util.stream.Stream; import jakarta.persistence.criteria.CriteriaBuilder; @@ -31,8 +30,8 @@ public class IdentityProviderWorkflowConditionProvider implements WorkflowCondit } @Override - public Set supportedTypes() { - return Set.of(ResourceType.USERS); + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/RoleWorkflowConditionProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/RoleWorkflowConditionProvider.java index 1e84f09a500..7c3f391d7e9 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/RoleWorkflowConditionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/RoleWorkflowConditionProvider.java @@ -33,8 +33,8 @@ public class RoleWorkflowConditionProvider implements WorkflowConditionProvider } @Override - public Set supportedTypes() { - return Set.of(ResourceType.USERS); + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/UserAttributeWorkflowConditionProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/UserAttributeWorkflowConditionProvider.java index 8a4e341508c..a6e7fd6a261 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/UserAttributeWorkflowConditionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/UserAttributeWorkflowConditionProvider.java @@ -4,7 +4,6 @@ import java.io.StringReader; import java.util.Arrays; import java.util.List; import java.util.Properties; -import java.util.Set; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; @@ -35,8 +34,8 @@ public class UserAttributeWorkflowConditionProvider implements WorkflowCondition } @Override - public Set supportedTypes() { - return Set.of(ResourceType.USERS); + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/EventEvaluator.java b/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/EventEvaluator.java deleted file mode 100644 index ec07384b6db..00000000000 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/EventEvaluator.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.keycloak.models.workflow.conditions.expression; - -import org.keycloak.models.workflow.ResourceOperationType; -import org.keycloak.models.workflow.WorkflowEvent; - -public class EventEvaluator extends AbstractBooleanEvaluator { - - private final WorkflowEvent event; - - public EventEvaluator(WorkflowEvent event) { - this.event = event; - } - - @Override - public Boolean visitConditionCall(BooleanConditionParser.ConditionCallContext ctx) { - String name = ctx.Identifier().getText(); - ResourceOperationType operation = ResourceOperationType.valueOf(name.replace("-", "_").toUpperCase()); - String param = super.extractParameter(ctx.parameter()); - return operation.test(event, param); - } -} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventFactory.java new file mode 100644 index 00000000000..94ef81fe75d --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class ClientAuthenticatedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "client-authenticated"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new ClientAuthenticatedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventProvider.java new file mode 100644 index 00000000000..ccf04b038e6 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientAuthenticatedWorkflowEventProvider.java @@ -0,0 +1,24 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; + +public class ClientAuthenticatedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public ClientAuthenticatedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.CLIENTS; + } + + @Override + public boolean supports(Event event) { + return EventType.CLIENT_LOGIN.equals(event.getType()); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventFactory.java new file mode 100644 index 00000000000..8ddad6cc593 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class ClientCreatedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "client-created"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new ClientCreatedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventProvider.java new file mode 100644 index 00000000000..ca66fbd09b1 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/ClientCreatedWorkflowEventProvider.java @@ -0,0 +1,32 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.provider.ProviderEvent; + +public class ClientCreatedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public ClientCreatedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.CLIENTS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof ClientModel.ClientCreationEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof ClientModel.ClientCreationEvent cce) { + return cce.getCreatedClient().getId(); + } + return null; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventFactory.java new file mode 100644 index 00000000000..901603cff5b --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserAuthenticatedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-authenticated"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserAuthenticatedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventProvider.java new file mode 100644 index 00000000000..df1c1e73b43 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserAuthenticatedWorkflowEventProvider.java @@ -0,0 +1,40 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; + +public class UserAuthenticatedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserAuthenticatedWorkflowEventProvider(KeycloakSession session, String configParameter, String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(Event event) { + return EventType.LOGIN.equals(event.getType()); + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + // this is the case when the clientId is passed as a parameter to the event provider - like user-logged-in(account-console) + Event loginEvent = (Event) context.getEvent().getEvent(); + return loginEvent != null && configParameter.equals(loginEvent.getClientId()); + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventFactory.java new file mode 100644 index 00000000000..2540df6e714 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserCreatedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-created"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserCreatedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventProvider.java new file mode 100644 index 00000000000..d8e85f5a1d9 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserCreatedWorkflowEventProvider.java @@ -0,0 +1,32 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.events.admin.AdminEvent; +import org.keycloak.events.admin.OperationType; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; + +public class UserCreatedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserCreatedWorkflowEventProvider(KeycloakSession session, String configParameter, String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(Event event) { + return EventType.REGISTER.equals(event.getType()); + } + + @Override + public boolean supports(AdminEvent adminEvent) { + return org.keycloak.events.admin.ResourceType.USER.equals(adminEvent.getResourceType()) + && OperationType.CREATE.equals(adminEvent.getOperationType()); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventFactory.java new file mode 100644 index 00000000000..555ccc6dd8b --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventFactory.java @@ -0,0 +1,21 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserFedIdentityAddedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-federated-identity-added"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserFedIdentityAddedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } + +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventProvider.java new file mode 100644 index 00000000000..cc651c9c2a8 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityAddedWorkflowEventProvider.java @@ -0,0 +1,52 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.FederatedIdentityModel.FederatedIdentityCreatedEvent; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +public class UserFedIdentityAddedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserFedIdentityAddedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof FederatedIdentityCreatedEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof FederatedIdentityCreatedEvent fie) { + return fie.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + // this is the case when the idp alias is passed as a parameter to the event provider - like user-federated-identity-added(myidp) + ProviderEvent fedIdentityEvent = (ProviderEvent) context.getEvent().getEvent(); + if (fedIdentityEvent instanceof FederatedIdentityCreatedEvent fie) { + return configParameter.equals(fie.getFederatedIdentity().getIdentityProvider()); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventFactory.java new file mode 100644 index 00000000000..fe385b22bf2 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserFedIdentityRemovedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-federated-identity-removed"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserFedIdentityRemovedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventProvider.java new file mode 100644 index 00000000000..aa99802dcbd --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserFedIdentityRemovedWorkflowEventProvider.java @@ -0,0 +1,52 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.FederatedIdentityModel.FederatedIdentityRemovedEvent; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +public class UserFedIdentityRemovedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserFedIdentityRemovedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof FederatedIdentityRemovedEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof FederatedIdentityRemovedEvent fie) { + return fie.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + // this is the case when the idp alias is passed as a parameter to the event provider - like user-federated-identity-removed(myidp) + ProviderEvent fedIdentityEvent = (ProviderEvent) context.getEvent().getEvent(); + if (fedIdentityEvent instanceof FederatedIdentityRemovedEvent fie) { + return configParameter.equals(fie.getFederatedIdentity().getIdentityProvider()); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventFactory.java new file mode 100644 index 00000000000..099ece13e46 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserGroupMembershipAddedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-group-membership-added"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserGroupMembershipAddedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventProvider.java new file mode 100644 index 00000000000..c397d258724 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipAddedWorkflowEventProvider.java @@ -0,0 +1,58 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.GroupModel.GroupMemberJoinEvent; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +import static org.keycloak.models.utils.KeycloakModelUtils.GROUP_PATH_SEPARATOR; + +public class UserGroupMembershipAddedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserGroupMembershipAddedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof GroupMemberJoinEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof GroupMemberJoinEvent gme) { + return gme.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + String groupName = configParameter; + // this is the case when the group name is passed as a parameter to the event provider - like user-group-membership-added(mygroup) + if (!groupName.startsWith(GROUP_PATH_SEPARATOR)) + groupName = GROUP_PATH_SEPARATOR + groupName; + ProviderEvent groupEvent = (ProviderEvent) context.getEvent().getEvent(); + if (groupEvent instanceof GroupMemberJoinEvent joinEvent) { + return groupName.equals(KeycloakModelUtils.buildGroupPath(joinEvent.getGroup())); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventFactory.java new file mode 100644 index 00000000000..0198330dda7 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserGroupMembershipRemovedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-group-membership-removed"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserGroupMembershipRemovedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventProvider.java new file mode 100644 index 00000000000..8c8db85cb48 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserGroupMembershipRemovedWorkflowEventProvider.java @@ -0,0 +1,58 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.GroupModel.GroupMemberLeaveEvent; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +import static org.keycloak.models.utils.KeycloakModelUtils.GROUP_PATH_SEPARATOR; + +public class UserGroupMembershipRemovedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserGroupMembershipRemovedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof GroupMemberLeaveEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof GroupMemberLeaveEvent gme) { + return gme.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + String groupName = configParameter; + // this is the case when the group name is passed as a parameter to the event provider - like user-group-membership-removed(mygroup) + if (!groupName.startsWith(GROUP_PATH_SEPARATOR)) + groupName = GROUP_PATH_SEPARATOR + groupName; + ProviderEvent groupEvent = (ProviderEvent) context.getEvent().getEvent(); + if (groupEvent instanceof GroupMemberLeaveEvent leaveEvent) { + return groupName.equals(KeycloakModelUtils.buildGroupPath(leaveEvent.getGroup())); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventFactory.java new file mode 100644 index 00000000000..539ac667073 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserRoleGrantedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-role-granted"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserRoleGrantedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventProvider.java new file mode 100644 index 00000000000..248261210c4 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleGrantedWorkflowEventProvider.java @@ -0,0 +1,52 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RoleModel.RoleGrantedEvent; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +public class UserRoleGrantedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserRoleGrantedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof RoleGrantedEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof RoleGrantedEvent rge) { + return rge.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + // this is the case when the role name is passed as a parameter to the event provider - like user-role-granted(myrole) + ProviderEvent roleEvent = (ProviderEvent) context.getEvent().getEvent(); + if (roleEvent instanceof RoleGrantedEvent roleGrantedEvent) { + return configParameter.equals(roleGrantedEvent.getRole().getName()); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventFactory.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventFactory.java new file mode 100644 index 00000000000..c2c7f27d6d4 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventFactory.java @@ -0,0 +1,20 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.workflow.WorkflowEventProvider; +import org.keycloak.models.workflow.WorkflowEventProviderFactory; + +public class UserRoleRevokedWorkflowEventFactory implements WorkflowEventProviderFactory { + + public static final String ID = "user-role-revoked"; + + @Override + public WorkflowEventProvider create(KeycloakSession session, String configParameter) { + return new UserRoleRevokedWorkflowEventProvider(session, configParameter, this.getId()); + } + + @Override + public String getId() { + return ID; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventProvider.java b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventProvider.java new file mode 100644 index 00000000000..343b8dd419c --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/events/UserRoleRevokedWorkflowEventProvider.java @@ -0,0 +1,52 @@ +package org.keycloak.models.workflow.events; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RoleModel.RoleRevokedEvent; +import org.keycloak.models.workflow.AbstractWorkflowEventProvider; +import org.keycloak.models.workflow.ResourceType; +import org.keycloak.models.workflow.WorkflowExecutionContext; +import org.keycloak.provider.ProviderEvent; + +public class UserRoleRevokedWorkflowEventProvider extends AbstractWorkflowEventProvider { + + public UserRoleRevokedWorkflowEventProvider(final KeycloakSession session, final String configParameter, final String providerId) { + super(session, configParameter, providerId); + } + + @Override + public ResourceType getSupportedResourceType() { + return ResourceType.USERS; + } + + @Override + public boolean supports(ProviderEvent providerEvent) { + return providerEvent instanceof RoleRevokedEvent; + } + + @Override + protected String resolveResourceId(ProviderEvent providerEvent) { + if (providerEvent instanceof RoleRevokedEvent rre) { + return rre.getUser().getId(); + } + return null; + } + + @Override + public boolean evaluate(WorkflowExecutionContext context) { + if (!super.evaluate(context)) { + return false; + } + if (super.configParameter != null) { + // this is the case when the role name is passed as a parameter to the event provider - like user-role-revoked(myrole) + ProviderEvent roleEvent = (ProviderEvent) context.getEvent().getEvent(); + if (roleEvent instanceof RoleRevokedEvent roleRevokedEvent) { + return configParameter.equals(roleRevokedEvent.getRole().getName()); + } else { + return false; + } + } else { + // nothing else to check + return true; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/AbstractBooleanEvaluator.java b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/AbstractBooleanEvaluator.java similarity index 97% rename from model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/AbstractBooleanEvaluator.java rename to model/jpa/src/main/java/org/keycloak/models/workflow/expression/AbstractBooleanEvaluator.java index 488bc96e7c2..1e7395bacf9 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/AbstractBooleanEvaluator.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/AbstractBooleanEvaluator.java @@ -1,4 +1,4 @@ -package org.keycloak.models.workflow.conditions.expression; +package org.keycloak.models.workflow.expression; public abstract class AbstractBooleanEvaluator extends BooleanConditionParserBaseVisitor { diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionEvaluator.java b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionEvaluator.java similarity index 84% rename from model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionEvaluator.java rename to model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionEvaluator.java index 6980389728f..361d9253823 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionEvaluator.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionEvaluator.java @@ -1,4 +1,4 @@ -package org.keycloak.models.workflow.conditions.expression; +package org.keycloak.models.workflow.expression; import org.keycloak.models.KeycloakSession; import org.keycloak.models.workflow.WorkflowConditionProvider; @@ -19,7 +19,7 @@ public class ConditionEvaluator extends AbstractBooleanEvaluator { @Override public Boolean visitConditionCall(BooleanConditionParser.ConditionCallContext ctx) { String conditionName = ctx.Identifier().getText(); - WorkflowConditionProvider conditionProvider = getConditionProvider(session, conditionName, super.extractParameter(ctx.parameter())); + WorkflowConditionProvider conditionProvider = getConditionProvider(session, conditionName.replace("_", "-").toLowerCase(), super.extractParameter(ctx.parameter())); return conditionProvider.evaluate(context); } diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionNameCollector.java b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionNameCollector.java similarity index 97% rename from model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionNameCollector.java rename to model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionNameCollector.java index bc9a5cc9936..a2b0904c220 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionNameCollector.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionNameCollector.java @@ -1,4 +1,4 @@ -package org.keycloak.models.workflow.conditions.expression; +package org.keycloak.models.workflow.expression; import java.util.ArrayList; import java.util.List; diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionParserUtil.java b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionParserUtil.java similarity index 94% rename from model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionParserUtil.java rename to model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionParserUtil.java index 523db67b70b..3b9b72b54b6 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionParserUtil.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionParserUtil.java @@ -1,4 +1,4 @@ -package org.keycloak.models.workflow.conditions.expression; +package org.keycloak.models.workflow.expression; final class ConditionParserUtil { diff --git a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionTypeCollector.java b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionTypeCollector.java similarity index 86% rename from model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionTypeCollector.java rename to model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionTypeCollector.java index d08d984abdd..95906e77155 100644 --- a/model/jpa/src/main/java/org/keycloak/models/workflow/conditions/expression/ConditionTypeCollector.java +++ b/model/jpa/src/main/java/org/keycloak/models/workflow/expression/ConditionTypeCollector.java @@ -1,7 +1,8 @@ -package org.keycloak.models.workflow.conditions.expression; +package org.keycloak.models.workflow.expression; import java.util.Collections; import java.util.EnumSet; +import java.util.List; import java.util.Set; import org.keycloak.models.KeycloakSession; @@ -9,7 +10,7 @@ import org.keycloak.models.workflow.ResourceType; import org.keycloak.models.workflow.WorkflowConditionProvider; import static org.keycloak.models.workflow.Workflows.getConditionProvider; -import static org.keycloak.models.workflow.conditions.expression.ConditionParserUtil.extractParameter; +import static org.keycloak.models.workflow.expression.ConditionParserUtil.extractParameter; /** * This visitor traverses the entire parse tree and collects the supported types of all conditionCalls. @@ -45,7 +46,7 @@ public class ConditionTypeCollector extends BooleanConditionParserBaseVisitor eventTypes; - private final List deactivationTypes; - private final BiPredicate conditionPredicate; - - ResourceOperationType(ResourceType resourceType, List eventTypes) { - this.resourceType = resourceType; - this.eventTypes = eventTypes; - this.deactivationTypes = List.of(); - this.conditionPredicate = defaultPredicate(); - } - - ResourceOperationType(ResourceType resourceType, List eventTypes, BiPredicate conditionPredicate) { - this.resourceType = resourceType; - this.eventTypes = eventTypes; - this.deactivationTypes = List.of(); - this.conditionPredicate = defaultPredicate().and(conditionPredicate); - } - - public ResourceType getResourceType() { - return resourceType; - } - - public static ResourceOperationType toOperationType(Enum from) { - return toOperationType(null, from); - } - - public static ResourceOperationType toOperationType(Class from) { - return toOperationType(null, from); - } - - public static ResourceOperationType toOperationType(ResourceType resourceType, Object from) { - for (ResourceOperationType value : values()) { - if (resourceType != null && !resourceType.equals(value.resourceType)) { - continue; - } - - if (value.eventTypes.contains(from)) { - return value; - } - for (Object type : value.eventTypes) { - Class fromClass = from instanceof Class ? (Class) from : from.getClass(); - if (type instanceof Class cls && cls.isAssignableFrom(fromClass)) { - return value; - } - } - } - - return null; - } - - public String getResourceId(ProviderEvent event) { - if (event instanceof GroupMemberJoinEvent gme) { - return gme.getUser().getId(); - } - if (event instanceof GroupMemberLeaveEvent gme) { - return gme.getUser().getId(); - } - if (event instanceof FederatedIdentityModel.FederatedIdentityCreatedEvent fie) { - return fie.getUser().getId(); - } - if (event instanceof FederatedIdentityModel.FederatedIdentityRemovedEvent fie) { - return fie.getUser().getId(); - } - if (event instanceof RoleGrantedEvent rge) { - return rge.getUser().getId(); - } - if (event instanceof RoleRevokedEvent rre) { - return rre.getUser().getId(); - } - return null; - } - - public boolean test(WorkflowEvent event, String detail) { - return conditionPredicate.test(event, detail); - } - - private BiPredicate defaultPredicate() { - return (event, detail) -> event.getOperation().equals(this); - } - - private static BiPredicate userLoginPredicate() { - return (event, detail) -> { - if (detail != null) { - Event loginEvent = (Event) event.getEvent(); - return detail.equals(loginEvent.getClientId()); - } else { - return true; - } - }; - } - - private static BiPredicate groupMembershipPredicate() { - return (event, groupName) -> { - if (groupName != null) { - if (!groupName.startsWith(GROUP_PATH_SEPARATOR)) - groupName = GROUP_PATH_SEPARATOR + groupName; - ProviderEvent groupEvent = (ProviderEvent) event.getEvent(); - if (groupEvent instanceof GroupMemberJoinEvent joinEvent) { - return groupName.equals(KeycloakModelUtils.buildGroupPath(joinEvent.getGroup())); - } else if (groupEvent instanceof GroupModel.GroupMemberLeaveEvent leaveEvent) { - return groupName.equals(KeycloakModelUtils.buildGroupPath(leaveEvent.getGroup())); - } else { - return false; - } - } else { - return true; - } - }; - } - - private static BiPredicate roleMembershipPredicate() { - return (event, roleName) -> { - if (roleName != null) { - ProviderEvent roleEvent = (ProviderEvent) event.getEvent(); - if (roleEvent instanceof RoleGrantedEvent roleGrantedEvent) { - return roleName.equals(roleGrantedEvent.getRole().getName()); - } else if (roleEvent instanceof RoleModel.RoleRevokedEvent roleRevokedEvent) { - return roleName.equals(roleRevokedEvent.getRole().getName()); - } else { - return false; - } - } else { - return true; - } - }; - } - - private static BiPredicate fedIdentityPredicate() { - return (event, idpAlias) -> { - if (idpAlias != null) { - ProviderEvent fedIdentityEvent = (ProviderEvent) event.getEvent(); - if (fedIdentityEvent instanceof FederatedIdentityModel.FederatedIdentityCreatedEvent fedIdentityCreatedEvent) { - return idpAlias.equals(fedIdentityCreatedEvent.getFederatedIdentity().getIdentityProvider()); - } else if (fedIdentityEvent instanceof FederatedIdentityRemovedEvent fedIdentityRemovedEvent) { - return idpAlias.equals(fedIdentityRemovedEvent.getFederatedIdentity().getIdentityProvider()); - } else { - return false; - } - } else { - return true; - } - }; - } - -} diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/ResourceType.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/ResourceType.java index c7be5bccddd..04dff378f4e 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/ResourceType.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/ResourceType.java @@ -18,52 +18,52 @@ package org.keycloak.models.workflow; -import java.util.List; import java.util.function.BiFunction; -import org.keycloak.events.EventType; -import org.keycloak.events.admin.OperationType; +import org.keycloak.events.Event; +import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; public enum ResourceType { USERS( - org.keycloak.events.admin.ResourceType.USER, - List.of(OperationType.CREATE), - List.of(EventType.LOGIN, EventType.REGISTER), - (session, id) -> session.users().getUserById(session.getContext().getRealm(), id) + (session, id) -> session.users().getUserById(session.getContext().getRealm(), id), + (session, event) -> event.getUserId() ), CLIENTS( - org.keycloak.events.admin.ResourceType.CLIENT, - List.of(OperationType.CREATE), - List.of(EventType.CLIENT_LOGIN, EventType.CLIENT_REGISTER), - (session, id) -> session.clients().getClientById(session.getContext().getRealm(), id) + (session, id) -> session.clients().getClientById(session.getContext().getRealm(), id), + (session, event) -> findClientResourceId(session, event.getClientId()) ); - private final org.keycloak.events.admin.ResourceType supportedAdminResourceType; - private final List supportedAdminOperationTypes; - private final List supportedEventTypes; private final BiFunction resourceResolver; + private final BiFunction resourceIdResolver; - ResourceType(org.keycloak.events.admin.ResourceType supportedAdminResourceType, - List supportedAdminOperationTypes, - List supportedEventTypes, - BiFunction resourceResolver) { - this.supportedAdminResourceType = supportedAdminResourceType; - this.supportedAdminOperationTypes = supportedAdminOperationTypes; - this.supportedEventTypes = supportedEventTypes; + ResourceType(BiFunction resourceResolver, + BiFunction resourceIdResolver) { this.resourceResolver = resourceResolver; - } - - public boolean supportsEvent(EventType eventType) { - return supportedEventTypes.contains(eventType); - } - - public boolean supportsAdminEvent(org.keycloak.events.admin.ResourceType resourceType, OperationType operationType) { - return supportedAdminResourceType.equals(resourceType) && supportedAdminOperationTypes.contains(operationType); + this.resourceIdResolver = resourceIdResolver; } public Object resolveResource(KeycloakSession session, String id) { return resourceResolver.apply(session, id); } + + public String resolveResourceId(KeycloakSession session, Event event) { + return resourceIdResolver.apply(session, event); + } + + private static String findClientResourceId(KeycloakSession session, String clientClientId) { + RealmModel realm = session.getContext().getRealm(); + if (realm == null) { + return null; + } + + ClientModel client = realm.getClientByClientId(clientClientId); + if (client == null) { + return null; + } + + return client.getId(); + } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflow.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflow.java index f7655b28641..5ad37d50ea4 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflow.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflow.java @@ -180,8 +180,8 @@ public class Workflow { addStep(step); // update allowed types - WorkflowStepProviderFactory stepProvider = getStepProviderFactory(step); - allowedTypes.retainAll(stepProvider.getTypes()); + WorkflowStepProviderFactory stepProvider = Workflows.getStepProviderFactory(session, step); + allowedTypes.retainAll(stepProvider.getSupportedResourceTypes()); } if (allowedTypes.isEmpty()) { @@ -225,9 +225,4 @@ public class Workflow { } return component; } - - private WorkflowStepProviderFactory getStepProviderFactory(WorkflowStep step) { - return (WorkflowStepProviderFactory) session - .getKeycloakSessionFactory().getProviderFactory(WorkflowStepProvider.class, step.getProviderId()); - } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProvider.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProvider.java index 21349c36b2e..93df660eacf 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProvider.java @@ -1,6 +1,5 @@ package org.keycloak.models.workflow; -import java.util.Set; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; @@ -11,7 +10,7 @@ import org.keycloak.provider.Provider; public interface WorkflowConditionProvider extends Provider { - Set supportedTypes(); + ResourceType getSupportedResourceType(); boolean evaluate(WorkflowExecutionContext context); diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProviderFactory.java index 295f9ed37a3..dc3996f13f6 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowConditionProviderFactory.java @@ -13,7 +13,7 @@ public interface WorkflowConditionProviderFactory

config) instead."); + throw new IllegalStateException("Use create(KeycloakSession session, String configParameter) instead."); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEvent.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEvent.java index 4d788a782c5..cd9d717cbfb 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEvent.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEvent.java @@ -3,29 +3,29 @@ package org.keycloak.models.workflow; public class WorkflowEvent { private final ResourceType type; - private final ResourceOperationType operation; private final String resourceId; private final Object event; + private final String eventProviderId; - public WorkflowEvent(ResourceType type, ResourceOperationType operation, String resourceId, Object event) { + public WorkflowEvent(ResourceType type, String resourceId, Object event, String eventProviderId) { this.type = type; - this.operation = operation; this.resourceId = resourceId; this.event = event; + this.eventProviderId = eventProviderId; } public ResourceType getResourceType() { return type; } - public ResourceOperationType getOperation() { - return operation; - } - public String getResourceId() { return resourceId; } + public String getEventProviderId() { + return eventProviderId; + } + public Object getEvent() { return event; } diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventConverter.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventConverter.java deleted file mode 100644 index 41c1606fe17..00000000000 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventConverter.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.keycloak.models.workflow; - -import java.util.Arrays; -import java.util.Optional; - -import org.keycloak.events.Event; -import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.ClientModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.provider.ProviderEvent; - -import static org.keycloak.models.workflow.ResourceOperationType.toOperationType; - -public final class WorkflowEventConverter { - - static Optional fromEvent(KeycloakSession session, Event event) { - return resourceOfEvent(event) - .map(r -> { - ResourceOperationType resourceOperationType = toOperationType(event.getType()); - String resourceId = switch (r) { - case USERS -> event.getUserId(); - case CLIENTS -> findClientResourceId(session, event.getClientId()); - }; - if (resourceOperationType != null && resourceId != null) { - return new WorkflowEvent(r, resourceOperationType, resourceId, event); - } - return null; - }); - } - - static Optional fromEvent(AdminEvent event) { - return resourceOfEvent(event) - .map(r -> { - ResourceOperationType resourceOperationType = toOperationType(r, event.getOperationType()); - if (resourceOperationType != null) { - return new WorkflowEvent(r, resourceOperationType, event.getResourceId(), event); - } - return null; - }); - } - - static Optional fromEvent(ProviderEvent event) { - ResourceOperationType resourceOperationType = toOperationType(event.getClass()); - if (resourceOperationType == null) { - return Optional.empty(); - } - - String resourceId = resourceOperationType.getResourceId(event); - if (resourceId == null) { - return Optional.empty(); - } - - WorkflowEvent workflowEvent = new WorkflowEvent(resourceOperationType.getResourceType(), resourceOperationType, resourceId, event); - return Optional.of(workflowEvent); - } - - - private static Optional resourceOfEvent(Event event) { - // Is it possible for an event to have multiple resources, thus triggering multiple workflows? - return Arrays.stream(ResourceType.values()) - .filter(r -> r.supportsEvent(event.getType())) - .findFirst(); - } - - private static Optional resourceOfEvent(AdminEvent event) { - return Arrays.stream(ResourceType.values()) - .filter(r -> r.supportsAdminEvent(event.getResourceType(), event.getOperationType())) - .findFirst(); - } - - private static String findClientResourceId(KeycloakSession session, String clientClientId) { - RealmModel realm = session.getContext().getRealm(); - if (realm == null) { - return null; - } - - ClientModel client = realm.getClientByClientId(clientClientId); - if (client == null) { - return null; - } - - return client.getId(); - } -} diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProvider.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProvider.java new file mode 100644 index 00000000000..f6bd6671018 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProvider.java @@ -0,0 +1,31 @@ +package org.keycloak.models.workflow; + +import org.keycloak.events.Event; +import org.keycloak.events.admin.AdminEvent; +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderEvent; + +public interface WorkflowEventProvider extends Provider { + + ResourceType getSupportedResourceType(); + + WorkflowEvent create(Event event); + + WorkflowEvent create(AdminEvent adminEvent); + + WorkflowEvent create(ProviderEvent providerEvent); + + boolean supports(Event event); + + boolean supports(AdminEvent adminEvent); + + boolean supports(ProviderEvent providerEvent); + + boolean evaluate(WorkflowExecutionContext context); + + @Override + default void close() { + // no-op + } + +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProviderFactory.java new file mode 100644 index 00000000000..52e33a23cd0 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventProviderFactory.java @@ -0,0 +1,38 @@ +package org.keycloak.models.workflow; + +import org.keycloak.Config; +import org.keycloak.common.Profile; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.EnvironmentDependentProviderFactory; +import org.keycloak.provider.ProviderFactory; + +public interface WorkflowEventProviderFactory

extends ProviderFactory

, EnvironmentDependentProviderFactory { + + P create(KeycloakSession session, String configParameter); + + @Override + default P create(KeycloakSession session) { + return create(session, null); + } + + @Override + default boolean isSupported(Config.Scope config) { + return Profile.isFeatureEnabled(Profile.Feature.WORKFLOWS); + } + + @Override + default void init(Config.Scope config) { + // no-op default + } + + @Override + default void postInit(KeycloakSessionFactory factory) { + // no-op default + } + + @Override + default void close() { + // no-op default + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventSpi.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventSpi.java new file mode 100644 index 00000000000..ced797b736e --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowEventSpi.java @@ -0,0 +1,31 @@ +package org.keycloak.models.workflow; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +public class WorkflowEventSpi implements Spi { + + public static final String NAME = "workflow-event"; + + @Override + public boolean isInternal() { + return true; + } + + @Override + public String getName() { + return NAME; + } + + + @Override + public Class getProviderClass() { + return WorkflowEventProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return WorkflowEventProviderFactory.class; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProvider.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProvider.java index 6a46496df98..afc8ee04a0b 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProvider.java @@ -22,7 +22,7 @@ import org.keycloak.provider.Provider; public interface WorkflowStepProvider extends Provider { /** - * Run this workflow step. + * Runs this workflow step. * * @param context the workflow execution context */ diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProviderFactory.java index 47c95fea1b5..3b544229442 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/WorkflowStepProviderFactory.java @@ -32,7 +32,7 @@ public interface WorkflowStepProviderFactory

ext /** * Supported types, usually one type but could be more (RestartStep for example) */ - Set getTypes(); + Set getSupportedResourceTypes(); @Override default void init(Config.Scope config) { diff --git a/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflows.java b/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflows.java index 8b901d89fab..4a8a7cd1c6a 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflows.java +++ b/server-spi-private/src/main/java/org/keycloak/models/workflow/Workflows.java @@ -3,22 +3,24 @@ package org.keycloak.models.workflow; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; +import org.keycloak.provider.Provider; public final class Workflows { - public static WorkflowConditionProvider getConditionProvider(KeycloakSession session, String name, String expression) { - return getConditionProviderFactory(session, name).create(session, expression); + public static WorkflowConditionProvider getConditionProvider(KeycloakSession session, String name, String configParameter) { + return getConditionProviderFactory(session, name).create(session, configParameter); } - private static WorkflowConditionProviderFactory getConditionProviderFactory(KeycloakSession session, String providerId) { - KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); - WorkflowConditionProviderFactory providerFactory = (WorkflowConditionProviderFactory) sessionFactory.getProviderFactory(WorkflowConditionProvider.class, providerId); + public static WorkflowConditionProviderFactory getConditionProviderFactory(KeycloakSession session, String providerId) { + return getProviderFactory(session, WorkflowConditionProvider.class, providerId); + } - if (providerFactory == null) { - throw new WorkflowInvalidStateException("Could not find condition provider: " + providerId); - } + public static WorkflowEventProvider getEventProvider(KeycloakSession session, String name, String configParameter) { + return getEventProviderFactory(session, name).create(session, configParameter); + } - return providerFactory; + public static WorkflowEventProviderFactory getEventProviderFactory(KeycloakSession session, String providerId) { + return getProviderFactory(session, WorkflowEventProvider.class, providerId); } public static WorkflowStepProvider getStepProvider(KeycloakSession session, WorkflowStep step) { @@ -27,13 +29,17 @@ public final class Workflows { } public static WorkflowStepProviderFactory getStepProviderFactory(KeycloakSession session, WorkflowStep step) { - WorkflowStepProviderFactory factory = (WorkflowStepProviderFactory) session - .getKeycloakSessionFactory().getProviderFactory(WorkflowStepProvider.class, step.getProviderId()); + return getProviderFactory(session, WorkflowStepProvider.class, step.getProviderId()); + } - if (factory == null) { - throw new WorkflowInvalidStateException("Step not found: " + step.getProviderId()); + private static

F getProviderFactory(KeycloakSession session, Class

providerClass, String providerId) { + KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); + @SuppressWarnings("unchecked") + F providerFactory = (F) sessionFactory.getProviderFactory(providerClass, providerId); + + if (providerFactory == null) { + throw new WorkflowInvalidStateException("Could not find provider factory with id: " + providerId); } - - return factory; + return providerFactory; } } diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index 024973e1b81..2c92a2b733c 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -105,9 +105,10 @@ org.keycloak.cookie.CookieSpi org.keycloak.organization.OrganizationSpi org.keycloak.securityprofile.SecurityProfileSpi org.keycloak.logging.MappedDiagnosticContextSpi +org.keycloak.models.workflow.WorkflowConditionSpi +org.keycloak.models.workflow.WorkflowEventSpi org.keycloak.models.workflow.WorkflowStepSpi org.keycloak.models.workflow.WorkflowSpi -org.keycloak.models.workflow.WorkflowConditionSpi org.keycloak.protocol.oidc.rar.AuthorizationDetailsProcessorSpi org.keycloak.cache.AlternativeLookupSPI org.keycloak.cache.LocalCacheSPI diff --git a/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProviderFactory.java index f636cdf9797..46544c29add 100644 --- a/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProviderFactory.java @@ -20,7 +20,7 @@ public class AddRequiredActionStepProviderFactory implements WorkflowStepProvide } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/DeleteUserStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/DeleteUserStepProviderFactory.java index 0ad718b4b9f..0f49028d683 100644 --- a/services/src/main/java/org/keycloak/models/workflow/DeleteUserStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/DeleteUserStepProviderFactory.java @@ -37,7 +37,7 @@ public class DeleteUserStepProviderFactory implements WorkflowStepProviderFactor } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/DisableUserStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/DisableUserStepProviderFactory.java index 5b423bfa5c1..6614b213b0a 100644 --- a/services/src/main/java/org/keycloak/models/workflow/DisableUserStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/DisableUserStepProviderFactory.java @@ -37,7 +37,7 @@ public class DisableUserStepProviderFactory implements WorkflowStepProviderFacto } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/GrantRoleStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/GrantRoleStepProviderFactory.java index dae19662ee3..e51a8d8b1b1 100644 --- a/services/src/main/java/org/keycloak/models/workflow/GrantRoleStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/GrantRoleStepProviderFactory.java @@ -20,7 +20,7 @@ public class GrantRoleStepProviderFactory implements WorkflowStepProviderFactory } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/JoinGroupStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/JoinGroupStepProviderFactory.java index afddb5716bb..024bc423f14 100644 --- a/services/src/main/java/org/keycloak/models/workflow/JoinGroupStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/JoinGroupStepProviderFactory.java @@ -20,7 +20,7 @@ public class JoinGroupStepProviderFactory implements WorkflowStepProviderFactory } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/LeaveGroupStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/LeaveGroupStepProviderFactory.java index 99d2d4dffbd..44c16110ed1 100644 --- a/services/src/main/java/org/keycloak/models/workflow/LeaveGroupStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/LeaveGroupStepProviderFactory.java @@ -20,7 +20,7 @@ public class LeaveGroupStepProviderFactory implements WorkflowStepProviderFactor } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/NotifyUserStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/NotifyUserStepProviderFactory.java index 383907847a1..24572e2522d 100644 --- a/services/src/main/java/org/keycloak/models/workflow/NotifyUserStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/NotifyUserStepProviderFactory.java @@ -37,7 +37,7 @@ public class NotifyUserStepProviderFactory implements WorkflowStepProviderFactor } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/RemoveRequiredActionStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/RemoveRequiredActionStepProviderFactory.java index 95785d2215a..93098dea24e 100644 --- a/services/src/main/java/org/keycloak/models/workflow/RemoveRequiredActionStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/RemoveRequiredActionStepProviderFactory.java @@ -20,7 +20,7 @@ public class RemoveRequiredActionStepProviderFactory implements WorkflowStepProv } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/RemoveUserAttributeStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/RemoveUserAttributeStepProviderFactory.java index 8808ad227f3..3120d41f244 100644 --- a/services/src/main/java/org/keycloak/models/workflow/RemoveUserAttributeStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/RemoveUserAttributeStepProviderFactory.java @@ -39,7 +39,7 @@ public class RemoveUserAttributeStepProviderFactory implements WorkflowStepProvi } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/RevokeRoleStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/RevokeRoleStepProviderFactory.java index a08afc2bc46..3a32e017324 100644 --- a/services/src/main/java/org/keycloak/models/workflow/RevokeRoleStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/RevokeRoleStepProviderFactory.java @@ -20,7 +20,7 @@ public class RevokeRoleStepProviderFactory implements WorkflowStepProviderFactor } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/SetUserAttributeStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/SetUserAttributeStepProviderFactory.java index 116335dee9c..2a542721010 100644 --- a/services/src/main/java/org/keycloak/models/workflow/SetUserAttributeStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/SetUserAttributeStepProviderFactory.java @@ -37,7 +37,7 @@ public class SetUserAttributeStepProviderFactory implements WorkflowStepProvider } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.USERS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/WorkflowEventListener.java b/services/src/main/java/org/keycloak/models/workflow/WorkflowEventListener.java index 002af1478a5..d2b989c0f76 100644 --- a/services/src/main/java/org/keycloak/models/workflow/WorkflowEventListener.java +++ b/services/src/main/java/org/keycloak/models/workflow/WorkflowEventListener.java @@ -10,6 +10,8 @@ package org.keycloak.models.workflow; +import java.util.Objects; + import org.keycloak.events.Event; import org.keycloak.events.EventListenerProvider; import org.keycloak.events.admin.AdminEvent; @@ -28,12 +30,18 @@ public class WorkflowEventListener implements EventListenerProvider, ProviderEve @Override public void onEvent(Event event) { - WorkflowEventConverter.fromEvent(session, event).ifPresent(this::trySchedule); + session.getAllProviders(WorkflowEventProvider.class).stream() + .map(provider -> provider.create(event)) + .filter(Objects::nonNull) + .forEach(this::trySchedule); } @Override public void onEvent(AdminEvent event, boolean includeRepresentation) { - WorkflowEventConverter.fromEvent(event).ifPresent(this::trySchedule); + session.getAllProviders(WorkflowEventProvider.class).stream() + .map(provider -> provider.create(event)) + .filter(Objects::nonNull) + .forEach(this::trySchedule); } @Override @@ -42,8 +50,10 @@ public class WorkflowEventListener implements EventListenerProvider, ProviderEve if (realm == null) { return; } - - WorkflowEventConverter.fromEvent(event).ifPresent(this::trySchedule); + session.getAllProviders(WorkflowEventProvider.class).stream() + .map(provider -> provider.create(event)) + .filter(Objects::nonNull) + .forEach(this::trySchedule); } private void trySchedule(WorkflowEvent event) { diff --git a/services/src/main/java/org/keycloak/models/workflow/client/DeleteClientStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/client/DeleteClientStepProviderFactory.java index bb4e2a6e354..b86428beb01 100644 --- a/services/src/main/java/org/keycloak/models/workflow/client/DeleteClientStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/client/DeleteClientStepProviderFactory.java @@ -40,7 +40,7 @@ public class DeleteClientStepProviderFactory implements } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.CLIENTS); } diff --git a/services/src/main/java/org/keycloak/models/workflow/client/DisableClientStepProviderFactory.java b/services/src/main/java/org/keycloak/models/workflow/client/DisableClientStepProviderFactory.java index 844114c7152..59723adb238 100644 --- a/services/src/main/java/org/keycloak/models/workflow/client/DisableClientStepProviderFactory.java +++ b/services/src/main/java/org/keycloak/models/workflow/client/DisableClientStepProviderFactory.java @@ -40,7 +40,7 @@ public class DisableClientStepProviderFactory implements } @Override - public Set getTypes() { + public Set getSupportedResourceTypes() { return Set.of(ResourceType.CLIENTS); } diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowManagementTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowManagementTest.java index b3d19359d26..68927c2b699 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowManagementTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowManagementTest.java @@ -34,7 +34,6 @@ import org.keycloak.admin.client.resource.WorkflowsResource; import org.keycloak.models.workflow.DeleteUserStepProviderFactory; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.ResourceType; import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; @@ -42,6 +41,8 @@ import org.keycloak.models.workflow.WorkflowStateProvider; import org.keycloak.models.workflow.WorkflowStateProvider.ScheduledStep; import org.keycloak.models.workflow.conditions.IdentityProviderWorkflowConditionFactory; import org.keycloak.models.workflow.conditions.RoleWorkflowConditionFactory; +import org.keycloak.models.workflow.events.UserAuthenticatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.ErrorRepresentation; import org.keycloak.representations.workflows.StepExecutionStatus; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -68,9 +69,6 @@ import com.fasterxml.jackson.jakarta.rs.yaml.JacksonYAMLProvider; import com.fasterxml.jackson.jakarta.rs.yaml.YAMLMediaTypes; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_AUTHENTICATED; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -103,7 +101,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { @Test public void testCreate() { WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -230,7 +228,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { @Test public void testFailCreateWorkflowWithNegativeTime() { WorkflowRepresentation workflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(SetUserAttributeStepProviderFactory.ID) .after(Duration.ofDays(-5)) @@ -247,7 +245,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { public void testFailCreateWorkflowWithDuplicateName() { // create first workflow managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(SetUserAttributeStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -257,7 +255,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { // try to create second workflow with same name try (Response response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.name()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(DisableUserStepProviderFactory.ID) .after(Duration.ofDays(10)) @@ -276,7 +274,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { String workflowId; try (Response response = workflows.create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -288,7 +286,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { } workflows.create(WorkflowRepresentation.withName("another-workflow") - .onEvent(ResourceOperationType.USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -511,7 +509,7 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { String[] workflowNames = {"alpha-workflow", "beta-workflow", "gamma-workflow", "delta-workflow"}; for (String name : workflowNames) { managedRealm.admin().workflows().create(WorkflowRepresentation.withName(name) - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowMigrationTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowMigrationTest.java index 9e59ffdf34a..dca9a071b2d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowMigrationTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowMigrationTest.java @@ -13,6 +13,8 @@ import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.WorkflowStateProvider; import org.keycloak.models.workflow.client.DeleteClientStepProviderFactory; import org.keycloak.models.workflow.conditions.UserAttributeWorkflowConditionFactory; +import org.keycloak.models.workflow.events.ClientCreatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.workflows.StepExecutionStatus; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -25,9 +27,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.CLIENT_ADDED; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -42,7 +41,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { public void testMigrationFailsIfWorkflowsNotCompatible() { // create two incompatible workflows - one that operates on users, another on clients var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("client-workflow") - .onEvent(CLIENT_ADDED.name()) + .onEvent(ClientCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DeleteClientStepProviderFactory.ID) @@ -54,7 +53,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { response.close(); response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("user-workflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(AddRequiredActionStepProviderFactory.ID) @@ -93,7 +92,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { public void testMigrationFailsIfResourcesDontMeetWorkflowConditions() { // create two user workflows, the second having a condition that the users don't meet var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow-1") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DisableUserStepProviderFactory.ID) @@ -144,7 +143,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { public void testMigrateToScheduledStepInDifferentWorkflow() { var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow-1") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DisableUserStepProviderFactory.ID) @@ -210,7 +209,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { @Test public void testMigrateToImmediateStepInDifferentWorkflow() { var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow-1") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DisableUserStepProviderFactory.ID) @@ -275,7 +274,7 @@ public class WorkflowMigrationTest extends AbstractWorkflowTest { @Test public void testMigrateToStepInSameWorkflow() { var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(AddRequiredActionStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/AdhocWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/AdhocWorkflowTest.java index 7c1d7bf810f..84e5b80a9d9 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/AdhocWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/AdhocWorkflowTest.java @@ -11,6 +11,7 @@ import org.keycloak.models.workflow.NotifyUserStepProviderFactory; import org.keycloak.models.workflow.ResourceType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.WorkflowProvider; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; @@ -22,8 +23,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; @@ -160,7 +159,7 @@ public class AdhocWorkflowTest extends AbstractWorkflowTest { @Test public void testDeactivateWorkflowForResource() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("One") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) @@ -174,7 +173,7 @@ public class AdhocWorkflowTest extends AbstractWorkflowTest { ) .build()).close(); managedRealm.admin().workflows().create(WorkflowRepresentation.withName("Two") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/GroupMembershipWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/GroupMembershipWorkflowTest.java index 4d32747a248..a11b459e541 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/GroupMembershipWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/GroupMembershipWorkflowTest.java @@ -7,8 +7,9 @@ import jakarta.ws.rs.core.Response.Status; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.WorkflowsResource; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.models.workflow.events.UserGroupMembershipAddedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserGroupMembershipRemovedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; @@ -52,7 +53,7 @@ public class GroupMembershipWorkflowTest extends AbstractWorkflowTest { } WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_GROUP_MEMBERSHIP_ADDED.name() + "(" + GROUP_NAME + ")") + .onEvent(UserGroupMembershipAddedWorkflowEventFactory.ID + "(" + GROUP_NAME + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) @@ -100,7 +101,7 @@ public class GroupMembershipWorkflowTest extends AbstractWorkflowTest { } WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_GROUP_MEMBERSHIP_REMOVED.name() + "(" + GROUP_NAME + ")") + .onEvent(UserGroupMembershipRemovedWorkflowEventFactory.ID + "(" + GROUP_NAME + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/IdpLinkingWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/IdpLinkingWorkflowTest.java index 605c71fe5bd..0f4bf455283 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/IdpLinkingWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/IdpLinkingWorkflowTest.java @@ -6,8 +6,9 @@ import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.WorkflowsResource; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.models.workflow.events.UserFedIdentityAddedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserFedIdentityRemovedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -42,7 +43,7 @@ public class IdpLinkingWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on IdP linking WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_FEDERATED_IDENTITY_ADDED.name() + "(" + IDP_ALIAS + ")") + .onEvent(UserFedIdentityAddedWorkflowEventFactory.ID + "(" + IDP_ALIAS + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) @@ -81,7 +82,7 @@ public class IdpLinkingWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on IdP unlinking WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_FEDERATED_IDENTITY_REMOVED.name() + "(" + IDP_ALIAS + ")") + .onEvent(UserFedIdentityRemovedWorkflowEventFactory.ID + "(" + IDP_ALIAS + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/RoleMembershipWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/RoleMembershipWorkflowTest.java index 0ad7a663934..3d638f1f8a0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/RoleMembershipWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/RoleMembershipWorkflowTest.java @@ -4,8 +4,9 @@ import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.WorkflowsResource; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.models.workflow.events.UserRoleGrantedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserRoleRevokedWorkflowEventFactory; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; @@ -45,7 +46,7 @@ public class RoleMembershipWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on role grant WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_ROLE_GRANTED.name() + "(" + ROLE_NAME + ")") + .onEvent(UserRoleGrantedWorkflowEventFactory.ID + "(" + ROLE_NAME + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) @@ -85,7 +86,7 @@ public class RoleMembershipWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on role revoke WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_ROLE_REVOKED.name() + "(" + ROLE_NAME + ")") + .onEvent(UserRoleRevokedWorkflowEventFactory.ID + "(" + ROLE_NAME + ")") .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserAuthenticationWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserAuthenticationWorkflowTest.java index a3affae297f..631322d4af5 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserAuthenticationWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserAuthenticationWorkflowTest.java @@ -26,6 +26,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; +import org.keycloak.models.workflow.events.UserAuthenticatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.InjectUser; @@ -41,8 +43,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_AUTHENTICATED; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; import static org.keycloak.tests.workflow.util.EmailTestUtils.findEmailByRecipient; import static org.keycloak.tests.workflow.util.EmailTestUtils.findEmailsByRecipient; import static org.keycloak.tests.workflow.util.EmailTestUtils.verifyEmailContent; @@ -68,7 +68,7 @@ public class UserAuthenticationWorkflowTest extends AbstractWorkflowTest { @Test public void testActivateWorkflowOnUserAuthentication() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().restartInProgress("true") // this setting enables restarting the workflow .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) @@ -143,7 +143,7 @@ public class UserAuthenticationWorkflowTest extends AbstractWorkflowTest { @Test public void testMultipleWorkflows() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.toString(), USER_AUTHENTICATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID, UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -152,7 +152,7 @@ public class UserAuthenticationWorkflowTest extends AbstractWorkflowTest { .build()) .build()).close(); managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow_2") - .onEvent(USER_CREATED.toString(), USER_AUTHENTICATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID, UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(10)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserCreationWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserCreationWorkflowTest.java index c92767c5ac9..a8c28e404ea 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserCreationWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/activation/UserCreationWorkflowTest.java @@ -3,8 +3,8 @@ package org.keycloak.tests.workflow.activation; import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.resource.UserResource; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -35,7 +35,7 @@ public class UserCreationWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on user creation WorkflowRepresentation workflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/GroupWorkflowConditionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/GroupWorkflowConditionTest.java index 645e0dc0167..072a2cb3e7e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/GroupWorkflowConditionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/GroupWorkflowConditionTest.java @@ -10,12 +10,12 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; 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.conditions.GroupMembershipWorkflowConditionFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -56,7 +56,7 @@ public class GroupWorkflowConditionTest extends AbstractWorkflowTest { // create workflow that activates on user creation with a group membership condition managedRealm.admin().workflows().create(WorkflowRepresentation.withName("group-membership-workflow") - .onEvent(ResourceOperationType.USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .onCondition(GROUP_CONDITION) .withSteps( WorkflowStepRepresentation.create() diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/IdpLinkConditionWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/IdpLinkConditionWorkflowTest.java index 767d5ba0aad..795a432669f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/IdpLinkConditionWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/IdpLinkConditionWorkflowTest.java @@ -28,7 +28,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.Workflow; import org.keycloak.models.workflow.WorkflowProvider; @@ -36,6 +35,8 @@ import org.keycloak.models.workflow.WorkflowStateProvider; import org.keycloak.models.workflow.WorkflowStateProvider.ScheduledStep; import org.keycloak.models.workflow.WorkflowStep; import org.keycloak.models.workflow.conditions.IdentityProviderWorkflowConditionFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserFedIdentityAddedWorkflowEventFactory; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; @@ -53,8 +54,6 @@ import org.keycloak.testsuite.util.IdentityProviderBuilder; import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -84,7 +83,7 @@ public class IdpLinkConditionWorkflowTest extends AbstractWorkflowTest { // create the workflow that triggers on IdP linking with a condition for the specific IdP WorkflowRepresentation workflow = WorkflowRepresentation.withName("idp-members-workflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .onCondition(IdentityProviderWorkflowConditionFactory.ID + "(" + IDP_OIDC_ALIAS + ")") .withSteps( WorkflowStepRepresentation.create() @@ -133,7 +132,7 @@ public class IdpLinkConditionWorkflowTest extends AbstractWorkflowTest { setupIdentityProvider(); managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_FEDERATED_IDENTITY_ADDED.name()) + .onEvent(UserFedIdentityAddedWorkflowEventFactory.ID) .onCondition(IdentityProviderWorkflowConditionFactory.ID + "(" + IDP_OIDC_ALIAS + ")") .schedule(WorkflowScheduleRepresentation.create().after("1s").build()) .withSteps( diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/RoleWorkflowConditionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/RoleWorkflowConditionTest.java index 2887a7381b5..05a2272c617 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/RoleWorkflowConditionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/RoleWorkflowConditionTest.java @@ -12,7 +12,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.Workflow; @@ -20,6 +19,7 @@ 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.models.workflow.events.UserRoleGrantedWorkflowEventFactory; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; @@ -172,7 +172,7 @@ public class RoleWorkflowConditionTest extends AbstractWorkflowTest { .reduce((a, b) -> a + " AND " + b).orElse(null); WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_ROLE_GRANTED.name()) + .onEvent(UserRoleGrantedWorkflowEventFactory.ID) .onCondition(roleCondition) .withSteps( WorkflowStepRepresentation.create() diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/UserAttributeWorkflowConditionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/UserAttributeWorkflowConditionTest.java index 2fa6d41b72e..b33fee69868 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/condition/UserAttributeWorkflowConditionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/condition/UserAttributeWorkflowConditionTest.java @@ -11,7 +11,6 @@ import jakarta.ws.rs.core.Response.Status; import org.keycloak.admin.client.resource.WorkflowsResource; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.Workflow; @@ -19,6 +18,7 @@ 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.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -161,7 +161,7 @@ public class UserAttributeWorkflowConditionTest extends AbstractWorkflowTest { .orElse(null); WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .schedule(WorkflowScheduleRepresentation.create().after("1s").build()) .onCondition(attributeCondition) .withSteps( diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/BrokeredUserLifecycleWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/BrokeredUserLifecycleWorkflowTest.java index 505c1f718d7..ef2b7c45bc2 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/BrokeredUserLifecycleWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/BrokeredUserLifecycleWorkflowTest.java @@ -13,8 +13,11 @@ import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DeleteUserStepProviderFactory; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.conditions.IdentityProviderWorkflowConditionFactory; +import org.keycloak.models.workflow.events.UserAuthenticatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserFedIdentityAddedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserFedIdentityRemovedWorkflowEventFactory; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -55,8 +58,6 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_AUTHENTICATED; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; import static org.keycloak.tests.workflow.util.EmailTestUtils.findEmailByRecipient; import static org.keycloak.tests.workflow.util.EmailTestUtils.verifyEmailContent; @@ -129,7 +130,7 @@ public class BrokeredUserLifecycleWorkflowTest extends AbstractWorkflowTest { // create a workflow that notifies inactive users after 7 days, disables them 30 days after that if the user doesn't // log back in, and finally deletes them also 30 days after being disabled. consumerRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .onCondition(IDP_CONDITION) .concurrency().restartInProgress("true") .withSteps( @@ -198,7 +199,7 @@ public class BrokeredUserLifecycleWorkflowTest extends AbstractWorkflowTest { public void testNonBrokeredUserNotAffectedByWorkflow() { // create a workflow that deletes inactive users after 10 days. consumerRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .onCondition(IDP_CONDITION) .withSteps( WorkflowStepRepresentation.create().of(DeleteUserStepProviderFactory.ID) @@ -221,7 +222,7 @@ public class BrokeredUserLifecycleWorkflowTest extends AbstractWorkflowTest { public void testInvalidateWorkflowOnIdentityProviderRemoval() { String workflowId; try (Response response = consumerRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.toString(), USER_AUTHENTICATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID, UserAuthenticatedWorkflowEventFactory.ID) .onCondition(IDP_CONDITION) .withSteps( WorkflowStepRepresentation.create().of(DeleteUserStepProviderFactory.ID) @@ -266,8 +267,8 @@ public class BrokeredUserLifecycleWorkflowTest extends AbstractWorkflowTest { // create a workflow that deletes users 1 day after a federated identity is added, and that is cancelled if the identity is removed consumerRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_FEDERATED_IDENTITY_ADDED.name() + "(" + IDP_OIDC_ALIAS + ")") - .concurrency().cancelInProgress(ResourceOperationType.USER_FEDERATED_IDENTITY_REMOVED.name() + "(" + IDP_OIDC_ALIAS + ")") + .onEvent(UserFedIdentityAddedWorkflowEventFactory.ID + "(" + IDP_OIDC_ALIAS + ")") + .concurrency().cancelInProgress(UserFedIdentityRemovedWorkflowEventFactory.ID + "(" + IDP_OIDC_ALIAS + ")") .withSteps( WorkflowStepRepresentation.create().of(DeleteUserStepProviderFactory.ID) .after(Duration.ofDays(1)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/DisableActiveWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/DisableActiveWorkflowTest.java index 62e27702eea..b169abcf0c0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/DisableActiveWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/DisableActiveWorkflowTest.java @@ -11,10 +11,10 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -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.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; @@ -52,7 +52,7 @@ public class DisableActiveWorkflowTest extends AbstractWorkflowTest { // create a test workflow String workflowId; try (Response response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("test-workflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ImmediateWorkflowExecutionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ImmediateWorkflowExecutionTest.java index fb5046e62de..3c7e09adb02 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ImmediateWorkflowExecutionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ImmediateWorkflowExecutionTest.java @@ -3,6 +3,7 @@ package org.keycloak.tests.workflow.execution; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; @@ -12,8 +13,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -29,7 +28,7 @@ public class ImmediateWorkflowExecutionTest extends AbstractWorkflowTest { public void testRunImmediateWorkflow() { // create a test workflow with no time conditions - should run immediately when scheduled managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(SetUserAttributeStepProviderFactory.ID) .withConfig("message", "message") diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ScheduledWorkflowExecutionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ScheduledWorkflowExecutionTest.java index 94f99075c38..3752dc2d1d3 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ScheduledWorkflowExecutionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/ScheduledWorkflowExecutionTest.java @@ -10,11 +10,11 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -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.WorkflowStep; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; @@ -44,7 +44,7 @@ public class ScheduledWorkflowExecutionTest extends AbstractWorkflowTest { @Test public void testWorkflowDoesNotFallThroughStepsInSingleRun() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/StepRunnerScheduledTaskTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/StepRunnerScheduledTaskTest.java index 868fb6ebb10..f7d00a7849c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/StepRunnerScheduledTaskTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/StepRunnerScheduledTaskTest.java @@ -32,6 +32,7 @@ import org.keycloak.models.UserProvider; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.WorkflowStepRunnerSuccessEvent; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.provider.ProviderEventListener; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -45,8 +46,6 @@ import org.keycloak.tests.workflow.config.WorkflowsScheduledTaskServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.junit.jupiter.api.Assertions.assertTrue; @KeycloakIntegrationTest(config = WorkflowsScheduledTaskServerConfig.class) @@ -73,7 +72,7 @@ public class StepRunnerScheduledTaskTest extends AbstractWorkflowTest { RealmResource realm = adminClient.realm(realmName); realm.workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(SetUserAttributeStepProviderFactory.ID) .after(Duration.ofDays(5)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/WorkflowConcurrencyTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/WorkflowConcurrencyTest.java index 356000b2455..dfaf5094d6e 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/execution/WorkflowConcurrencyTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/execution/WorkflowConcurrencyTest.java @@ -10,6 +10,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; import org.keycloak.models.workflow.WorkflowStateProvider; +import org.keycloak.models.workflow.events.UserAuthenticatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.InjectUser; @@ -25,8 +26,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_AUTHENTICATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -47,7 +46,7 @@ public class WorkflowConcurrencyTest extends AbstractWorkflowTest { public void testWorkflowIsRestartedOnSameEvent() { // create a workflow that can be restarted on the same event - i.e. has concurrency setting with restart-in-progress=true managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().restartInProgress("true") .withSteps( WorkflowStepRepresentation.create() @@ -89,7 +88,7 @@ public class WorkflowConcurrencyTest extends AbstractWorkflowTest { // create a workflow that can be restarted on a different event - i.e. restart-in-progress is set to an event expression // in this case we will use user-group-membership-added event to restart the workflow when user joins the group "testgroup" managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().restartInProgress("user-group-membership-added(testgroup)") .withSteps( WorkflowStepRepresentation.create() @@ -112,7 +111,7 @@ public class WorkflowConcurrencyTest extends AbstractWorkflowTest { public void testWorkflowIsCancelledOnSameEvent() { // create a workflow that can be cancelled on the same event - i.e. has concurrency setting with cancel-in-progress=true managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().cancelInProgress("true") .withSteps( WorkflowStepRepresentation.create() @@ -154,7 +153,7 @@ public class WorkflowConcurrencyTest extends AbstractWorkflowTest { // create a workflow that can be cancelled on a different event - i.e. cancel-in-progress is set to an event expression // in this case we will use user-group-membership-added event to cancel the workflow when user joins the group "testgroup" managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().cancelInProgress("user-group-membership-added(testgroup)") .withSteps( WorkflowStepRepresentation.create() @@ -189,7 +188,7 @@ public class WorkflowConcurrencyTest extends AbstractWorkflowTest { // create workflow with both settings - restart-in-progress on same event, cancel-in-progress on different event managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .concurrency().restartInProgress("true") .cancelInProgress("user-group-membership-added(testgroup)") .withSteps( diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/AddRequiredActionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/AddRequiredActionTest.java index 26858a2adea..24f7156b9d6 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/AddRequiredActionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/AddRequiredActionTest.java @@ -5,6 +5,7 @@ import java.time.Duration; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.AddRequiredActionStepProvider; import org.keycloak.models.workflow.AddRequiredActionStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; @@ -16,8 +17,6 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -31,7 +30,7 @@ public class AddRequiredActionTest extends AbstractWorkflowTest { @Test public void testStepRun() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(AddRequiredActionStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteClientStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteClientStepTest.java index cccdd70c0ff..090306a299c 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteClientStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteClientStepTest.java @@ -1,4 +1,4 @@ -package org.keycloak.tests.admin.model.workflow; +package org.keycloak.tests.workflow.step; import java.time.Duration; @@ -9,6 +9,8 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory; import org.keycloak.models.workflow.client.DeleteClientStepProviderFactory; import org.keycloak.models.workflow.client.DisableClientStepProviderFactory; +import org.keycloak.models.workflow.events.ClientAuthenticatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.ClientCreatedWorkflowEventFactory; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; @@ -21,9 +23,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.CLIENT_ADDED; -import static org.keycloak.models.workflow.ResourceOperationType.CLIENT_LOGGED_IN; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -42,7 +41,7 @@ public class DeleteClientStepTest extends AbstractWorkflowTest { @Test public void testStepRun() { var response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(CLIENT_ADDED.name()) + .onEvent(ClientCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DeleteClientStepProviderFactory.ID) @@ -75,7 +74,7 @@ public class DeleteClientStepTest extends AbstractWorkflowTest { @Test public void testDisabledClientAfterInactivityPeriod() { WorkflowRepresentation workflowRepresentation = WorkflowRepresentation.withName("myworkflow") - .onEvent(CLIENT_ADDED.toString(), CLIENT_LOGGED_IN.toString()) + .onEvent(ClientCreatedWorkflowEventFactory.ID, ClientAuthenticatedWorkflowEventFactory.ID) .concurrency() .withSteps( WorkflowStepRepresentation.create().of(DisableClientStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserWorkflowStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserStepTest.java similarity index 96% rename from tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserWorkflowStepTest.java rename to tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserStepTest.java index 9c1280563c6..90258b61afb 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserWorkflowStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/DeleteUserStepTest.java @@ -28,11 +28,11 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.workflow.DeleteUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; 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.events.UserAuthenticatedWorkflowEventFactory; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -52,7 +52,7 @@ import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; import org.keycloak.testframework.util.ApiUtil; import org.keycloak.tests.workflow.AbstractWorkflowTest; import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; -import org.keycloak.tests.workflow.step.DeleteUserWorkflowStepTest.DeleteUserWorkflowServerConf; +import org.keycloak.tests.workflow.step.DeleteUserStepTest.DeleteUserWorkflowServerConf; import org.keycloak.testsuite.federation.DummyUserFederationProvider; import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; @@ -75,7 +75,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * Tests the execution of the 'delete-user' workflow step. */ @KeycloakIntegrationTest(config = DeleteUserWorkflowServerConf.class) -public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest { +public class DeleteUserStepTest extends AbstractWorkflowTest { @InjectWebDriver ManagedWebDriver driver; @@ -110,7 +110,7 @@ public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest { } managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .withSteps(builder.build()).build()).close(); String componentId = addDummyFederationProvider(); @@ -170,7 +170,7 @@ public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest { // create a couple of workflows that will activate for the test user // the first one will run the delete user step before the second one runs its first step managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow1") - .onEvent(ResourceOperationType.USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(DeleteUserStepProviderFactory.ID) @@ -178,7 +178,7 @@ public class DeleteUserWorkflowStepTest extends AbstractWorkflowTest { .build() ).build()).close(); managedRealm.admin().workflows().create(WorkflowRepresentation.withName("workflow2") - .onEvent(ResourceOperationType.USER_AUTHENTICATED.toString()) + .onEvent(UserAuthenticatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(SetUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/GroupBasedStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/GroupBasedStepTest.java index 0259353bb99..6a382a112d6 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/GroupBasedStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/GroupBasedStepTest.java @@ -15,7 +15,8 @@ import org.keycloak.models.workflow.JoinGroupStepProvider; import org.keycloak.models.workflow.JoinGroupStepProviderFactory; import org.keycloak.models.workflow.LeaveGroupStepProvider; import org.keycloak.models.workflow.LeaveGroupStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserGroupMembershipRemovedWorkflowEventFactory; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -31,8 +32,6 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -62,7 +61,7 @@ public class GroupBasedStepTest extends AbstractWorkflowTest { List expectedGroups = List.of("/a", "/b/b1", "c"); create(WorkflowRepresentation.withName("join-group") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(JoinGroupStepProviderFactory.ID) @@ -95,7 +94,7 @@ public class GroupBasedStepTest extends AbstractWorkflowTest { joinGroup(user, "a", "/a/a1", "b/b1", "b/b2", "/c"); create(WorkflowRepresentation.withName("leave-group") - .onEvent(ResourceOperationType.USER_GROUP_MEMBERSHIP_REMOVED.name()) + .onEvent(UserGroupMembershipRemovedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(LeaveGroupStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/NotificationStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/NotificationStepTest.java index d9dc60187b8..f02d43070bf 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/NotificationStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/NotificationStepTest.java @@ -30,6 +30,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.workflow.DeleteUserStepProviderFactory; import org.keycloak.models.workflow.DisableUserStepProviderFactory; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.StepExecutionStatus; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; @@ -47,7 +48,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; import static org.keycloak.tests.workflow.util.EmailTestUtils.findEmailByRecipient; import static org.keycloak.tests.workflow.util.EmailTestUtils.findEmailByRecipientContaining; import static org.keycloak.tests.workflow.util.EmailTestUtils.verifyEmailContent; @@ -80,7 +80,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { public void testNotifyUserStepSendsEmailWithDefaultDisableMessage() { // Create workflow: disable at 10 days, notify 3 days before (at day 7) managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(7)) @@ -108,7 +108,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { public void testNotifyUserStepSendsEmailWithDefaultDeleteMessage() { // Create workflow: delete at 30 days, notify 15 days before (at day 15) managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(15)) @@ -135,7 +135,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { @Test public void testNotifyUserStepSkipsUsersWithoutEmailButLogsWarning() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -169,7 +169,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { public void testCompleteUserLifecycleWithMultipleNotifications() { // Create workflow: just disable at 30 days with one notification before managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(15)) @@ -218,7 +218,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { public void testNotifyUserStepWithCustomMessageOverride() throws IOException { // Create workflow: disable at 7 days, notify 2 days before (at day 5) with custom message managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .withConfig("message", "

Dear ${user.firstName} ${user.lastName},

\n" + @@ -265,7 +265,7 @@ public class NotificationStepTest extends AbstractWorkflowTest { public void testNotifyUserStepWithSendToConfiguration() throws Exception { // Create workflow: notify immediately with send_to managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .withConfig("to", "admin@example.com") diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveRequiredActionTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveRequiredActionTest.java index 60a3e382f19..5e9eb8fb110 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveRequiredActionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveRequiredActionTest.java @@ -5,6 +5,7 @@ import java.time.Duration; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.RemoveRequiredActionStepProvider; import org.keycloak.models.workflow.RemoveRequiredActionStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; @@ -16,8 +17,6 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; @@ -30,7 +29,7 @@ public class RemoveRequiredActionTest extends AbstractWorkflowTest { @Test public void testStepRun() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("remove-action-workflow") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(RemoveRequiredActionStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveUserAttributeStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveUserAttributeStepTest.java index 697a571c2bb..8ce3f20253a 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveUserAttributeStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RemoveUserAttributeStepTest.java @@ -5,6 +5,7 @@ import java.time.Duration; import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -18,8 +19,6 @@ import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -34,7 +33,7 @@ public class RemoveUserAttributeStepTest extends AbstractWorkflowTest { // create workflow that removes attributes 'a' and 'b' on user creation create(WorkflowRepresentation.withName("remove-attrs") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(org.keycloak.models.workflow.RemoveUserAttributeStepProviderFactory.ID) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RestartStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RestartStepTest.java index 62e33499858..92138099f10 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RestartStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RestartStepTest.java @@ -7,13 +7,13 @@ import jakarta.ws.rs.core.Response; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.workflow.NotifyUserStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory; 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.WorkflowStep; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; import org.keycloak.representations.idm.ErrorRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; @@ -50,7 +50,7 @@ public class RestartStepTest extends AbstractWorkflowTest { @Test public void testRestartWorkflow() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -119,7 +119,7 @@ public class RestartStepTest extends AbstractWorkflowTest { @Test public void testRestartFromPosition() { try (Response response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -136,7 +136,7 @@ public class RestartStepTest extends AbstractWorkflowTest { is("No scheduled step found if restarting at position 1")); } try (Response response = managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -153,7 +153,7 @@ public class RestartStepTest extends AbstractWorkflowTest { is("No scheduled step found if restarting at position 2")); } managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) @@ -253,7 +253,7 @@ public class RestartStepTest extends AbstractWorkflowTest { @Test public void testRestartFromLastStep() { managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") - .onEvent(ResourceOperationType.USER_CREATED.toString()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) .after(Duration.ofDays(5)) diff --git a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RoleBasedStepTest.java b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RoleBasedStepTest.java index 171b8a4d2c5..a9a38cfb704 100644 --- a/tests/base/src/test/java/org/keycloak/tests/workflow/step/RoleBasedStepTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/workflow/step/RoleBasedStepTest.java @@ -13,9 +13,10 @@ import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.models.workflow.GrantRoleStepProvider; import org.keycloak.models.workflow.GrantRoleStepProviderFactory; -import org.keycloak.models.workflow.ResourceOperationType; import org.keycloak.models.workflow.RevokeRoleStepProvider; import org.keycloak.models.workflow.RevokeRoleStepProviderFactory; +import org.keycloak.models.workflow.events.UserCreatedWorkflowEventFactory; +import org.keycloak.models.workflow.events.UserRoleRevokedWorkflowEventFactory; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -33,8 +34,6 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasItems; @@ -65,7 +64,7 @@ public class RoleBasedStepTest extends AbstractWorkflowTest { List expectedRoles = Stream.concat(expectedRealmRoles.stream(), expectedClientRoles.stream()).toList(); create(WorkflowRepresentation.withName("grant-roles") - .onEvent(USER_CREATED.name()) + .onEvent(UserCreatedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(GrantRoleStepProviderFactory.ID) @@ -96,7 +95,7 @@ public class RoleBasedStepTest extends AbstractWorkflowTest { grantRole(user, "realm-role-a", "realm-role-b", "realm-role-c", "myclient/client-role-a", "myclient/client-role-c"); create(WorkflowRepresentation.withName("revoke-roles") - .onEvent(ResourceOperationType.USER_ROLE_REVOKED.name()) + .onEvent(UserRoleRevokedWorkflowEventFactory.ID) .withSteps( WorkflowStepRepresentation.create() .of(RevokeRoleStepProviderFactory.ID)