Add action that removes a required action step in workflow

Closes #44647

Signed-off-by: ksushant881 <ksushant881@gmail.com>
This commit is contained in:
ksushant881 2026-01-05 18:50:12 +05:30 committed by Pedro Igor
parent 0885062c37
commit 5939864b76
4 changed files with 164 additions and 1 deletions

View File

@ -0,0 +1,43 @@
package org.keycloak.models.workflow;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.jboss.logging.Logger;
public class RemoveRequiredActionStepProvider implements WorkflowStepProvider {
public static String REQUIRED_ACTION_KEY = "action";
private final KeycloakSession session;
private final ComponentModel stepModel;
private final Logger log = Logger.getLogger(RemoveRequiredActionStepProvider.class);
public RemoveRequiredActionStepProvider(KeycloakSession session, ComponentModel model) {
this.session = session;
this.stepModel = model;
}
@Override
public void run(WorkflowExecutionContext context) {
RealmModel realm = session.getContext().getRealm();
UserModel user = session.users().getUserById(realm, context.getResourceId());
if (user != null) {
try {
UserModel.RequiredAction action = UserModel.RequiredAction.valueOf(stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY));
log.debugv("Removing required action {0} from user {1})", action, user.getId());
user.removeRequiredAction(action);
} catch (IllegalArgumentException e) {
log.warnv("Invalid required action {0} configured in RemoveRequiredActionStepProvider", stepModel.getConfig().getFirst(REQUIRED_ACTION_KEY));
}
}
}
@Override
public void close() {
}
}

View File

@ -0,0 +1,63 @@
package org.keycloak.models.workflow;
import java.util.List;
import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
public class RemoveRequiredActionStepProviderFactory implements WorkflowStepProviderFactory<RemoveRequiredActionStepProvider>, ConfiguredProvider {
public static final String ID = "remove-user-required-action";
@Override
public RemoveRequiredActionStepProvider create(KeycloakSession session, ComponentModel model) {
return new RemoveRequiredActionStepProvider(session, model);
}
@Override
public void init(Config.Scope config) {
// no-op
}
@Override
public void postInit(KeycloakSessionFactory factory) {
// no-op
}
@Override
public void close() {
// no-op
}
@Override
public String getId() {
return ID;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return ProviderConfigurationBuilder.create()
.property()
.name("action")
.label("Required Action")
.helpText("The required action to remove from the user (e.g., UPDATE_PASSWORD)")
.type(ProviderConfigProperty.STRING_TYPE)
.add()
.build();
}
@Override
public ResourceType getType() {
return ResourceType.USERS;
}
@Override
public String getHelpText() {
return "";
}
}

View File

@ -23,4 +23,5 @@ org.keycloak.models.workflow.AddRequiredActionStepProviderFactory
org.keycloak.models.workflow.GrantRoleStepProviderFactory
org.keycloak.models.workflow.RevokeRoleStepProviderFactory
org.keycloak.models.workflow.JoinGroupStepProviderFactory
org.keycloak.models.workflow.LeaveGroupStepProviderFactory
org.keycloak.models.workflow.LeaveGroupStepProviderFactory
org.keycloak.models.workflow.RemoveRequiredActionStepProviderFactory

View File

@ -0,0 +1,56 @@
package org.keycloak.tests.workflow.step;
import java.time.Duration;
import org.keycloak.models.UserModel;
import org.keycloak.models.workflow.RemoveRequiredActionStepProvider;
import org.keycloak.models.workflow.RemoveRequiredActionStepProviderFactory;
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.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.Test;
import static org.keycloak.models.workflow.ResourceOperationType.USER_CREATED;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
/**
* Tests the execution of the 'remove-required-action' workflow step.
*/
@KeycloakIntegrationTest(config = WorkflowsBlockingServerConfig.class)
public class RemoveRequiredActionTest extends AbstractWorkflowTest {
@Test
public void testStepRun() {
managedRealm.admin().workflows().create(WorkflowRepresentation.withName("remove-action-workflow")
.onEvent(USER_CREATED.name())
.withSteps(
WorkflowStepRepresentation.create()
.of(RemoveRequiredActionStepProviderFactory.ID)
.withConfig(RemoveRequiredActionStepProvider.REQUIRED_ACTION_KEY, "UPDATE_PASSWORD")
.build()
).build()).close();
managedRealm.admin().users().create(UserConfigBuilder.create()
.username("testuser_remove")
.requiredActions(UserModel.RequiredAction.UPDATE_PASSWORD.name())
.build()).close();
Awaitility.await()
.timeout(Duration.ofSeconds(30))
.pollInterval(Duration.ofSeconds(1))
.untilAsserted(() -> {
var users = managedRealm.admin().users().search("testuser_remove");
assertThat(users, hasSize(1));
var userRepresentation = users.get(0);
Assertions.assertTrue(userRepresentation.getRequiredActions() == null || userRepresentation.getRequiredActions().isEmpty());
});
}
}