From fa3a1fab4b10fb820aa514d204c128ef2e24ddca Mon Sep 17 00:00:00 2001 From: Stefan Guilhen Date: Tue, 3 Feb 2026 10:41:46 -0300 Subject: [PATCH] Ensure required action is enabled at the realm level before adding it to the user via workflow step Closes #45976 Signed-off-by: Stefan Guilhen --- .../AddRequiredActionStepProvider.java | 16 ++++++++-- .../workflow/step/AddRequiredActionTest.java | 32 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProvider.java b/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProvider.java index 37b83bfe9c5..4b865721444 100644 --- a/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProvider.java +++ b/services/src/main/java/org/keycloak/models/workflow/AddRequiredActionStepProvider.java @@ -27,12 +27,24 @@ public class AddRequiredActionStepProvider implements WorkflowStepProvider { UserModel user = session.users().getUserById(realm, context.getResourceId()); if (user != null) { + String configuredAction = stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY); + if (configuredAction == null) { + log.warnv("Missing required configuration option '{0}' in {1}", REQUIRED_ACTION_KEY, AddRequiredActionStepProviderFactory.ID); + return; + } try { - UserModel.RequiredAction action = UserModel.RequiredAction.valueOf(stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY)); + // Convert hyphens to underscores and uppercase to match enum naming + configuredAction = configuredAction.replace("-", "_").toUpperCase(); + UserModel.RequiredAction action = UserModel.RequiredAction.valueOf(configuredAction); + if (!realm.getRequiredActionProviderByAlias(action.name()).isEnabled()) { + log.warnv("Required action {0} is not enabled in realm {1}", action, realm.getName()); + return; + } log.debugv("Adding required action {0} to user {1})", action, user.getId()); user.addRequiredAction(action); } catch (IllegalArgumentException e) { - log.warnv("Invalid required action {0} configured in AddRequiredActionStepProvider", stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY)); + log.warnv("Invalid required action {0} configured in {1}", stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY), + AddRequiredActionStepProviderFactory.ID); } } } 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 24f7156b9d6..eed5361e5e4 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 @@ -2,19 +2,24 @@ package org.keycloak.tests.workflow.step; import java.time.Duration; +import jakarta.ws.rs.core.Response; + 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.idm.UserRepresentation; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.UserConfigBuilder; +import org.keycloak.testframework.util.ApiUtil; import org.keycloak.tests.workflow.AbstractWorkflowTest; import org.keycloak.tests.workflow.config.WorkflowsBlockingServerConfig; import org.awaitility.Awaitility; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -34,7 +39,7 @@ public class AddRequiredActionTest extends AbstractWorkflowTest { .withSteps( WorkflowStepRepresentation.create() .of(AddRequiredActionStepProviderFactory.ID) - .withConfig(AddRequiredActionStepProvider.REQUIRED_ACTION_KEY, "UPDATE_PASSWORD") + .withConfig(AddRequiredActionStepProvider.REQUIRED_ACTION_KEY, "update-password") .build() ).build()).close(); @@ -52,4 +57,29 @@ public class AddRequiredActionTest extends AbstractWorkflowTest { assertThat(userRepresentation.getRequiredActions().get(0), is(UserModel.RequiredAction.UPDATE_PASSWORD.name())); }); } + + @Test + @DisplayName("Test that a disabled required action is not added to the user") + public void testDisabledRequiredActionIsNotAdded() { + // create a workflow that adds 'terms-and-conditions' required action - it is disabled by default in the realm, so step should not add it to the user + managedRealm.admin().workflows().create(WorkflowRepresentation.withName("myworkflow") + .onEvent(UserCreatedWorkflowEventFactory.ID) + .withSteps( + WorkflowStepRepresentation.create() + .of(AddRequiredActionStepProviderFactory.ID) + .withConfig(AddRequiredActionStepProvider.REQUIRED_ACTION_KEY, "terms-and-conditions") + .build() + ).build()).close(); + + // create a user to trigger the workflow + String userId; + try (Response response = managedRealm.admin().users().create(UserConfigBuilder.create().username("myuser").build())) { + assertThat(201, is(response.getStatus())); + userId = ApiUtil.getCreatedId(response); + } + + // check the user does not have the required action added + UserRepresentation userRepresentation = managedRealm.admin().users().get(userId).toRepresentation(); + assertThat(userRepresentation.getRequiredActions(), hasSize(0)); + } }