From b5ed45f2a088d521b2d0dcdc64872d3014eac199 Mon Sep 17 00:00:00 2001 From: vramik Date: Sun, 19 Oct 2025 18:32:44 +0200 Subject: [PATCH] Ability to define workflows with YAML Closes #42687 Signed-off-by: vramik Co-authored-by: Pedro Igor --- .../quarkus/deployment/KeycloakProcessor.java | 1 + quarkus/runtime/pom.xml | 6 +++ services/pom.xml | 5 +++ .../admin/resource/WorkflowsResource.java | 5 ++- .../workflow/WorkflowManagementTest.java | 41 +++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 9397b4dc988..fa3314b5968 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -787,6 +787,7 @@ class KeycloakProcessor { void index(BuildProducer indexDependencyBuildItemBuildProducer) { indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("org.liquibase", "liquibase-core")); indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("org.keycloak", "keycloak-services")); + indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("com.fasterxml.jackson.jakarta.rs", "jackson-jakarta-rs-yaml-provider")); } @BuildStep diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 8f65a451245..73d83c7c66c 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -655,6 +655,12 @@ jna + + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-yaml-provider + + junit junit diff --git a/services/pom.xml b/services/pom.xml index a5953440f05..26e092364b7 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -183,6 +183,11 @@ ${woodstox.version} test + + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-yaml-provider + com.google.zxing javase 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 31a0b3da2f3..1e8879e5e84 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 @@ -4,6 +4,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; +import com.fasterxml.jackson.jakarta.rs.yaml.YAMLMediaTypes; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.NotFoundException; @@ -37,7 +38,7 @@ public class WorkflowsResource { } @POST - @Consumes(MediaType.APPLICATION_JSON) + @Consumes({MediaType.APPLICATION_JSON, YAMLMediaTypes.APPLICATION_JACKSON_YAML}) public Response create(WorkflowRepresentation rep) { try { Workflow workflow = manager.toModel(rep); @@ -49,7 +50,7 @@ public class WorkflowsResource { @Path("set") @POST - @Consumes(MediaType.APPLICATION_JSON) + @Consumes({MediaType.APPLICATION_JSON, YAMLMediaTypes.APPLICATION_JACKSON_YAML}) public Response createAll(WorkflowSetRepresentation workflows) { for (WorkflowRepresentation workflow : Optional.ofNullable(workflows.getWorkflows()).orElse(List.of())) { create(workflow).close(); 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 4e3f6850afc..0245f1a4365 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 @@ -34,6 +34,9 @@ import java.util.Collections; import java.util.List; import java.util.UUID; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.Response; import org.hamcrest.Matchers; import jakarta.mail.MessagingException; @@ -42,6 +45,8 @@ import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.BearerAuthFilter; import org.keycloak.admin.client.resource.WorkflowsResource; import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory; import org.keycloak.common.util.Time; @@ -69,6 +74,8 @@ import org.keycloak.representations.workflows.WorkflowSetRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.representations.workflows.WorkflowConditionRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; +import org.keycloak.testframework.annotations.InjectAdminClient; +import org.keycloak.testframework.annotations.InjectKeycloakUrls; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.mail.MailServer; import org.keycloak.testframework.mail.annotations.InjectMailServer; @@ -79,7 +86,9 @@ import org.keycloak.testframework.realm.UserConfigBuilder; import org.keycloak.testframework.remote.providers.runonserver.RunOnServer; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; +import org.keycloak.testframework.server.KeycloakUrls; import org.keycloak.tests.utils.MailUtils; +import org.keycloak.util.JsonSerialization; @KeycloakIntegrationTest(config = WorkflowsServerConfig.class) public class WorkflowManagementTest { @@ -95,6 +104,12 @@ public class WorkflowManagementTest { @InjectMailServer private MailServer mailServer; + @InjectKeycloakUrls + KeycloakUrls keycloakUrls; + + @InjectAdminClient(ref = "managed", realmRef = "managedRealm") + Keycloak adminClient; + @Test public void testCreate() { WorkflowSetRepresentation expectedWorkflows = WorkflowRepresentation.create() @@ -877,6 +892,32 @@ public class WorkflowManagementTest { mailServer.runCleanup(); } + @Test + public void testCreateUsingYaml() throws IOException { + WorkflowSetRepresentation expectedWorkflows = WorkflowRepresentation.create() + .of(UserCreationTimeWorkflowProviderFactory.ID) + .withSteps( + WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID) + .after(Duration.ofDays(5)) + .build(), + WorkflowStepRepresentation.create().of(DisableUserStepProviderFactory.ID) + .after(Duration.ofDays(5)) + .build() + ).build(); + + Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true);; + WebTarget target = httpClient.target(keycloakUrls.getBaseUrl().toString()) + .path("admin") + .path("realms") + .path(managedRealm.getName()) + .path("workflows") + .path("set") + .register(new BearerAuthFilter(adminClient.tokenManager())); + + Response response = target.request().post(Entity.entity(JsonSerialization.writeValueAsString(expectedWorkflows), "application/yaml")); + response.close(); + } + public static List findEmailsByRecipient(MailServer mailServer, String expectedRecipient) { return Arrays.stream(mailServer.getReceivedMessages()) .filter(msg -> {