From 484980dbbef922ba9278b3c3184d0b6c22432bfb Mon Sep 17 00:00:00 2001 From: Stefan Guilhen Date: Wed, 3 Dec 2025 15:05:37 -0300 Subject: [PATCH] Add API method to allow activating a workflow for all eligible resources Closes #44643 Signed-off-by: Stefan Guilhen --- .../admin/client/resource/WorkflowResource.java | 15 ++++++++++----- .../workflow/admin/resource/WorkflowResource.java | 15 +++++++++++++-- .../admin/resource/WorkflowsResource.java | 4 ++-- .../workflow/GroupMembershipJoinWorkflowTest.java | 13 ++++--------- .../model/workflow/RoleWorkflowConditionTest.java | 13 ++++--------- .../UserAttributeWorkflowConditionTest.java | 12 ++++-------- .../model/workflow/WorkflowManagementTest.java | 15 ++++++--------- 7 files changed, 43 insertions(+), 44 deletions(-) diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/WorkflowResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/WorkflowResource.java index bd80eeefd6d..3f2f2806e87 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/WorkflowResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/WorkflowResource.java @@ -8,7 +8,7 @@ import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Response; import org.keycloak.representations.workflows.WorkflowRepresentation; @@ -28,19 +28,24 @@ public interface WorkflowResource { @Produces(APPLICATION_JSON) WorkflowRepresentation toRepresentation(); + @Path("activate-all") + @POST + void activateAll(); + + @Path("activate-all") + @POST + void activateAll(@QueryParam("notBefore") String notBefore); + @Path("activate/{type}/{resourceId}") @POST - @Consumes(MediaType.APPLICATION_JSON) void activate(@PathParam("type") String type, @PathParam("resourceId") String resourceId); @Path("activate/{type}/{resourceId}") @POST - @Consumes(MediaType.APPLICATION_JSON) - void activate(@PathParam("type") String type, @PathParam("resourceId") String resourceId, String notBefore); + void activate(@PathParam("type") String type, @PathParam("resourceId") String resourceId, @QueryParam("notBefore") String notBefore); @Path("deactivate/{type}/{resourceId}") @POST - @Consumes(MediaType.APPLICATION_JSON) void deactivate(@PathParam("type") String type, @PathParam("resourceId") String resourceId); } diff --git a/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowResource.java b/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowResource.java index 0d5d7bf1628..c04b705600f 100644 --- a/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowResource.java +++ b/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowResource.java @@ -46,6 +46,7 @@ public class WorkflowResource { * Update the workflow configuration. The method does not update the workflow steps. */ @PUT + @Consumes({YAMLMediaTypes.APPLICATION_JACKSON_YAML, MediaType.APPLICATION_JSON}) public void update(WorkflowRepresentation rep) { try { provider.updateWorkflow(workflow, rep); @@ -76,9 +77,8 @@ public class WorkflowResource { * an integer followed by 'ms' representing milliseconds from now, or an ISO-8601 date string. */ @POST - @Consumes(MediaType.APPLICATION_JSON) @Path("activate/{type}/{resourceId}") - public void activate(@PathParam("type") ResourceType type, @PathParam("resourceId") String resourceId, String notBefore) { + public void activate(@PathParam("type") ResourceType type, @PathParam("resourceId") String resourceId, @QueryParam("notBefore") String notBefore) { Object resource = provider.getResourceTypeSelector(type).resolveResource(resourceId); if (resource == null) { @@ -92,6 +92,17 @@ public class WorkflowResource { provider.activate(workflow, type, resourceId); } + @POST + @Path("activate-all") + public void activateAll(@QueryParam("notBefore") String notBefore) { + + if (notBefore != null) { + workflow.setNotBefore(notBefore); + } + + provider.activateForAllEligibleResources(workflow); + } + /** * Deactivate the workflow for the resource. * diff --git a/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowsResource.java b/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowsResource.java index 978ad228ff6..adf7a6db4ae 100644 --- a/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowsResource.java +++ b/services/src/main/java/org/keycloak/workflow/admin/resource/WorkflowsResource.java @@ -44,7 +44,7 @@ public class WorkflowsResource { } @POST - @Consumes({MediaType.APPLICATION_JSON, YAMLMediaTypes.APPLICATION_JACKSON_YAML}) + @Consumes({YAMLMediaTypes.APPLICATION_JACKSON_YAML, MediaType.APPLICATION_JSON}) public Response create(WorkflowRepresentation rep) { auth.realm().requireManageRealm(); @@ -70,7 +70,7 @@ public class WorkflowsResource { } @GET - @Produces({MediaType.APPLICATION_JSON, YAMLMediaTypes.APPLICATION_JACKSON_YAML}) + @Produces({YAMLMediaTypes.APPLICATION_JACKSON_YAML, MediaType.APPLICATION_JSON}) public List list( @Parameter(description = "A String representing the workflow name - either partial or exact") @QueryParam("search") String search, @Parameter(description = "Boolean which defines whether the param 'search' must match exactly or not") @QueryParam("exact") Boolean exact, diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/GroupMembershipJoinWorkflowTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/GroupMembershipJoinWorkflowTest.java index 2ffb3ff52d7..daa82aa187d 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/GroupMembershipJoinWorkflowTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/GroupMembershipJoinWorkflowTest.java @@ -163,15 +163,10 @@ public class GroupMembershipJoinWorkflowTest extends AbstractWorkflowTest { .build() ).build()).close(); - - runOnServer.run((RunOnServer) session -> { - // check the same users are now scheduled to run the second step. - WorkflowProvider provider = session.getProvider(WorkflowProvider.class); - List registeredWorkflows = provider.getWorkflows().toList(); - assertThat(registeredWorkflows, hasSize(1)); - // activate the workflow for all eligible users - provider.activateForAllEligibleResources(registeredWorkflows.get(0)); - }); + List workflows = managedRealm.admin().workflows().list(); + assertThat(workflows, hasSize(1)); + // activate the workflow for all eligible users + managedRealm.admin().workflows().workflow(workflows.get(0).getId()).activateAll(); runOnServer.run((RunOnServer) session -> { // check the same users are now scheduled to run the second step. diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/RoleWorkflowConditionTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/RoleWorkflowConditionTest.java index f825be4fec8..43a8ea0da71 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/RoleWorkflowConditionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/RoleWorkflowConditionTest.java @@ -95,15 +95,10 @@ public class RoleWorkflowConditionTest extends AbstractWorkflowTest { .build() ).build()).close(); - - runOnServer.run((RunOnServer) session -> { - // check the same users are now scheduled to run the second step. - WorkflowProvider provider = session.getProvider(WorkflowProvider.class); - List registeredWorkflows = provider.getWorkflows().toList(); - assertThat(registeredWorkflows, hasSize(1)); - // activate the workflow for all eligible users - provider.activateForAllEligibleResources(registeredWorkflows.get(0)); - }); + List workflows = managedRealm.admin().workflows().list(); + assertThat(workflows, hasSize(1)); + // activate the workflow for all eligible users + managedRealm.admin().workflows().workflow(workflows.get(0).getId()).activateAll(); runOnServer.run((RunOnServer) session -> { // check the same users are now scheduled to run the second step. diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/UserAttributeWorkflowConditionTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/UserAttributeWorkflowConditionTest.java index f0fc53f978c..b5f1be93a43 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/UserAttributeWorkflowConditionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/UserAttributeWorkflowConditionTest.java @@ -90,14 +90,10 @@ public class UserAttributeWorkflowConditionTest extends AbstractWorkflowTest { createWorkflow(Map.of("key", List.of("value"))); - runOnServer.run((RunOnServer) session -> { - // check the same users are now scheduled to run the second step. - WorkflowProvider provider = session.getProvider(WorkflowProvider.class); - List registeredWorkflows = provider.getWorkflows().toList(); - assertThat(registeredWorkflows, hasSize(1)); - // activate the workflow for all eligible users - provider.activateForAllEligibleResources(registeredWorkflows.get(0)); - }); + List workflows = managedRealm.admin().workflows().list(); + assertThat(workflows, hasSize(1)); + // activate the workflow for all eligible users + managedRealm.admin().workflows().workflow(workflows.get(0).getId()).activateAll(); runOnServer.run((RunOnServer) session -> { // check the same users are now scheduled to run the second step. diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/WorkflowManagementTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/WorkflowManagementTest.java index 22e9b996d3d..a64630d0c94 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/WorkflowManagementTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/model/workflow/WorkflowManagementTest.java @@ -626,15 +626,12 @@ public class WorkflowManagementTest extends AbstractWorkflowTest { assertTrue(user.getUsername().startsWith("new-idp-user-")); }); }); - runOnServer.run((RunOnServer) session -> { - // check the same users are now scheduled to run the second step. - WorkflowProvider provider = session.getProvider(WorkflowProvider.class); - List registeredWorkflows = provider.getWorkflows().toList(); - assertEquals(1, registeredWorkflows.size()); - Workflow workflow = registeredWorkflows.get(0); - // activate the workflow for all eligible users - i.e. only users from the same idp who are not yet assigned to the workflow. - provider.activateForAllEligibleResources(workflow); - }); + + List workflows = managedRealm.admin().workflows().list(); + assertThat(workflows, hasSize(1)); + // activate the workflow for all eligible users - i.e. only users from the same idp who are not yet assigned to the workflow. + managedRealm.admin().workflows().workflow(workflows.get(0).getId()).activateAll(); + runOnServer.run((RunOnServer) session -> { RealmModel realm = session.getContext().getRealm(); // check the same users are now scheduled to run the second step.