mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-19 13:30:21 -06:00
Add toPredicate implementation for conditions
Closes #42696 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
@@ -1,9 +1,16 @@
|
|||||||
package org.keycloak.models.workflow.conditions;
|
package org.keycloak.models.workflow.conditions;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
||||||
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
||||||
@@ -34,6 +41,29 @@ public class GroupMembershipWorkflowConditionProvider implements WorkflowConditi
|
|||||||
return user.isMemberOf(group);
|
return user.isMemberOf(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate toPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> path) {
|
||||||
|
validate();
|
||||||
|
|
||||||
|
GroupModel group = KeycloakModelUtils.findGroupByPath(session, session.getContext().getRealm(), expectedGroup);
|
||||||
|
if (group == null) {
|
||||||
|
return cb.disjunction(); // always false
|
||||||
|
}
|
||||||
|
|
||||||
|
Subquery<Integer> subquery = query.subquery(Integer.class);
|
||||||
|
Root<UserGroupMembershipEntity> from = subquery.from(UserGroupMembershipEntity.class);
|
||||||
|
|
||||||
|
subquery.select(cb.literal(1));
|
||||||
|
subquery.where(
|
||||||
|
cb.and(
|
||||||
|
cb.equal(from.get("user").get("id"), path.get("id")),
|
||||||
|
cb.equal(from.get("groupId"), group.getId())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return cb.exists(subquery);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (StringUtil.isBlank(this.expectedGroup)) {
|
if (StringUtil.isBlank(this.expectedGroup)) {
|
||||||
|
|||||||
@@ -3,11 +3,18 @@ package org.keycloak.models.workflow.conditions;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
||||||
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
||||||
@@ -40,6 +47,29 @@ public class RoleWorkflowConditionProvider implements WorkflowConditionProvider
|
|||||||
return role != null && RoleUtils.hasRole(roles, role);
|
return role != null && RoleUtils.hasRole(roles, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate toPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> path) {
|
||||||
|
validate();
|
||||||
|
|
||||||
|
RoleModel role = getRole(expectedRole, session.getContext().getRealm());
|
||||||
|
if (role == null) {
|
||||||
|
return cb.disjunction(); // always false
|
||||||
|
}
|
||||||
|
|
||||||
|
Subquery<Integer> subquery = query.subquery(Integer.class);
|
||||||
|
Root<UserRoleMappingEntity> from = subquery.from(UserRoleMappingEntity.class);
|
||||||
|
|
||||||
|
subquery.select(cb.literal(1));
|
||||||
|
subquery.where(
|
||||||
|
cb.and(
|
||||||
|
cb.equal(from.get("user").get("id"), path.get("id")),
|
||||||
|
cb.equal(from.get("roleId"), role.getId())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return cb.exists(subquery);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate() throws WorkflowInvalidStateException {
|
public void validate() throws WorkflowInvalidStateException {
|
||||||
if (StringUtil.isBlank(expectedRole)) {
|
if (StringUtil.isBlank(expectedRole)) {
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
package org.keycloak.models.workflow.conditions;
|
package org.keycloak.models.workflow.conditions;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
import org.keycloak.models.workflow.WorkflowConditionProvider;
|
||||||
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
import org.keycloak.models.workflow.WorkflowExecutionContext;
|
||||||
import org.keycloak.models.workflow.WorkflowInvalidStateException;
|
import org.keycloak.models.workflow.WorkflowInvalidStateException;
|
||||||
|
import org.keycloak.storage.jpa.JpaHashUtils;
|
||||||
|
|
||||||
import static org.keycloak.common.util.CollectionUtil.collectionEquals;
|
import static org.keycloak.common.util.CollectionUtil.collectionEquals;
|
||||||
|
|
||||||
@@ -41,6 +50,63 @@ public class UserAttributeWorkflowConditionProvider implements WorkflowCondition
|
|||||||
return collectionEquals(expectedValues, values);
|
return collectionEquals(expectedValues, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate toPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> path) {
|
||||||
|
validate();
|
||||||
|
|
||||||
|
String[] parsedKeyValuePair = parseKeyValuePair(expectedAttribute);
|
||||||
|
String attributeName = parsedKeyValuePair[0];
|
||||||
|
List<String> expectedValues = Arrays.asList(parsedKeyValuePair[1].split(","));
|
||||||
|
|
||||||
|
// Subquery to count how many of the expected values the user has
|
||||||
|
// to check if there is no missing value
|
||||||
|
Subquery<Long> matchingCountSubquery = query.subquery(Long.class);
|
||||||
|
Root<UserAttributeEntity> attrRoot1 = matchingCountSubquery.from(UserAttributeEntity.class);
|
||||||
|
matchingCountSubquery.select(cb.count(attrRoot1));
|
||||||
|
|
||||||
|
// Build predicate for matching values
|
||||||
|
// For values <= 255 chars: compare against 'value' field
|
||||||
|
// For values > 255 chars: compare against 'longValueHash' field (to avoid Oracle NCLOB comparison issues)
|
||||||
|
Predicate[] valuePredicates = expectedValues.stream()
|
||||||
|
.map(expectedValue -> {
|
||||||
|
if (expectedValue.length() > 255) {
|
||||||
|
// Use hash comparison for long values to avoid NCLOB comparison issues in Oracle
|
||||||
|
return cb.equal(attrRoot1.get("longValueHash"), JpaHashUtils.hashForAttributeValue(expectedValue));
|
||||||
|
} else {
|
||||||
|
// For short values, compare directly
|
||||||
|
return cb.equal(attrRoot1.get("value"), expectedValue);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toArray(Predicate[]::new);
|
||||||
|
|
||||||
|
matchingCountSubquery.where(
|
||||||
|
cb.and(
|
||||||
|
cb.equal(attrRoot1.get("user").get("id"), path.get("id")),
|
||||||
|
cb.equal(attrRoot1.get("name"), attributeName),
|
||||||
|
cb.or(valuePredicates)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Subquery to count total attributes with this name for the user
|
||||||
|
// to check if there are no extra values
|
||||||
|
Subquery<Long> totalCountSubquery = query.subquery(Long.class);
|
||||||
|
Root<UserAttributeEntity> attrRoot2 = totalCountSubquery.from(UserAttributeEntity.class);
|
||||||
|
totalCountSubquery.select(cb.count(attrRoot2));
|
||||||
|
totalCountSubquery.where(
|
||||||
|
cb.and(
|
||||||
|
cb.equal(attrRoot2.get("user").get("id"), path.get("id")),
|
||||||
|
cb.equal(attrRoot2.get("name"), attributeName)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Both counts must equal the expected count (exact match)
|
||||||
|
int expectedCount = expectedValues.size();
|
||||||
|
return cb.and(
|
||||||
|
cb.equal(matchingCountSubquery, expectedCount),
|
||||||
|
cb.equal(totalCountSubquery, expectedCount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (expectedAttribute == null) {
|
if (expectedAttribute == null) {
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ public interface WorkflowConditionProvider extends Provider {
|
|||||||
|
|
||||||
boolean evaluate(WorkflowExecutionContext context);
|
boolean evaluate(WorkflowExecutionContext context);
|
||||||
|
|
||||||
default Predicate toPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> resourceRoot) {
|
Predicate toPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> resourceRoot);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void validate() throws WorkflowInvalidStateException;
|
void validate() throws WorkflowInvalidStateException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import jakarta.ws.rs.core.Response.Status;
|
|||||||
|
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.admin.client.resource.WorkflowsResource;
|
import org.keycloak.admin.client.resource.WorkflowsResource;
|
||||||
|
import org.keycloak.models.workflow.DisableUserStepProviderFactory;
|
||||||
import org.keycloak.models.workflow.NotifyUserStepProviderFactory;
|
import org.keycloak.models.workflow.NotifyUserStepProviderFactory;
|
||||||
import org.keycloak.models.workflow.ResourceOperationType;
|
import org.keycloak.models.workflow.ResourceOperationType;
|
||||||
import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
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.conditions.GroupMembershipWorkflowConditionFactory;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
@@ -21,6 +25,7 @@ import org.keycloak.representations.workflows.WorkflowStepRepresentation;
|
|||||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
import org.keycloak.testframework.realm.GroupConfigBuilder;
|
import org.keycloak.testframework.realm.GroupConfigBuilder;
|
||||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||||
|
import org.keycloak.testframework.remote.providers.runonserver.RunOnServer;
|
||||||
import org.keycloak.testframework.util.ApiUtil;
|
import org.keycloak.testframework.util.ApiUtil;
|
||||||
|
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
@@ -135,4 +140,49 @@ public class GroupMembershipJoinWorkflowTest extends AbstractWorkflowTest {
|
|||||||
assertThat(status.getErrors().get(0), containsString("Group with name %s does not exist.".formatted("generic-group")));
|
assertThat(status.getErrors().get(0), containsString("Group with name %s does not exist.".formatted("generic-group")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActivateWorkflowForEligibleResources() {
|
||||||
|
managedRealm.admin().groups().add(GroupConfigBuilder.create().name("groupA").build()).close();
|
||||||
|
|
||||||
|
// create some users associated with a group membership
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
managedRealm.admin().users().create(UserConfigBuilder.create().username("group-member-" + i)
|
||||||
|
.groups("groupA").build()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
managedRealm.admin().workflows().create(WorkflowRepresentation.withName("groupA-membership-workflow")
|
||||||
|
.onEvent(ResourceOperationType.USER_GROUP_MEMBERSHIP_ADDED.name())
|
||||||
|
.onCondition(GroupMembershipWorkflowConditionFactory.ID + "(groupA)")
|
||||||
|
.withSteps(
|
||||||
|
WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID)
|
||||||
|
.after(Duration.ofDays(5))
|
||||||
|
.build(),
|
||||||
|
WorkflowStepRepresentation.create().of(DisableUserStepProviderFactory.ID)
|
||||||
|
.after(Duration.ofDays(10))
|
||||||
|
.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<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
// activate the workflow for all eligible users
|
||||||
|
provider.activateForAllEligibleResources(registeredWorkflows.get(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
runOnServer.run((RunOnServer) session -> {
|
||||||
|
// check the same users are now scheduled to run the second step.
|
||||||
|
WorkflowProvider provider = session.getProvider(WorkflowProvider.class);
|
||||||
|
List<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
Workflow workflow = registeredWorkflows.get(0);
|
||||||
|
// check workflow was correctly assigned to the users
|
||||||
|
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||||
|
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||||
|
assertThat(scheduledSteps, hasSize(10));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,14 @@ import org.keycloak.admin.client.resource.RolesResource;
|
|||||||
import org.keycloak.admin.client.resource.WorkflowsResource;
|
import org.keycloak.admin.client.resource.WorkflowsResource;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
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.ResourceOperationType;
|
||||||
import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory;
|
import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory;
|
||||||
import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
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.RoleWorkflowConditionFactory;
|
import org.keycloak.models.workflow.conditions.RoleWorkflowConditionFactory;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
@@ -23,12 +28,14 @@ import org.keycloak.representations.workflows.WorkflowStepRepresentation;
|
|||||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
import org.keycloak.testframework.realm.RoleConfigBuilder;
|
import org.keycloak.testframework.realm.RoleConfigBuilder;
|
||||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||||
|
import org.keycloak.testframework.remote.providers.runonserver.RunOnServer;
|
||||||
import org.keycloak.testframework.util.ApiUtil;
|
import org.keycloak.testframework.util.ApiUtil;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@@ -62,6 +69,54 @@ public class RoleWorkflowConditionTest extends AbstractWorkflowTest {
|
|||||||
assertUserRoles("user-3", true, expected);
|
assertUserRoles("user-3", true, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActivateWorkflowForEligibleResources() {
|
||||||
|
RoleRepresentation role = createRoleIfNotExists("testRole");
|
||||||
|
|
||||||
|
// create some users associated with the role
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
try (Response response = managedRealm.admin().users().create(UserConfigBuilder.create().username("user-with-role-" + i).build())) {
|
||||||
|
assertThat(response.getStatus(), is(Status.CREATED.getStatusCode()));
|
||||||
|
managedRealm.admin().users().get(ApiUtil.getCreatedId(response)).roles().realmLevel().add(List.of(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
managedRealm.admin().workflows().create(WorkflowRepresentation.withName("test-role-workflow")
|
||||||
|
.onEvent(ResourceOperationType.USER_ROLE_ADDED.name())
|
||||||
|
.onCondition(RoleWorkflowConditionFactory.ID + "(testRole)")
|
||||||
|
.withSteps(
|
||||||
|
WorkflowStepRepresentation.create().of(NotifyUserStepProviderFactory.ID)
|
||||||
|
.after(Duration.ofDays(5))
|
||||||
|
.build(),
|
||||||
|
WorkflowStepRepresentation.create().of(DisableUserStepProviderFactory.ID)
|
||||||
|
.after(Duration.ofDays(10))
|
||||||
|
.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<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
// activate the workflow for all eligible users
|
||||||
|
provider.activateForAllEligibleResources(registeredWorkflows.get(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
runOnServer.run((RunOnServer) session -> {
|
||||||
|
// check the same users are now scheduled to run the second step.
|
||||||
|
WorkflowProvider provider = session.getProvider(WorkflowProvider.class);
|
||||||
|
List<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
Workflow workflow = registeredWorkflows.get(0);
|
||||||
|
// check workflow was correctly assigned to the users
|
||||||
|
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||||
|
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||||
|
assertThat(scheduledSteps, hasSize(10));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void assertUserRoles(String username, boolean shouldExist, String... roles) {
|
private void assertUserRoles(String username, boolean shouldExist, String... roles) {
|
||||||
assertUserRoles(username, shouldExist, List.of(roles));
|
assertUserRoles(username, shouldExist, List.of(roles));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import org.keycloak.models.UserModel;
|
|||||||
import org.keycloak.models.workflow.ResourceOperationType;
|
import org.keycloak.models.workflow.ResourceOperationType;
|
||||||
import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory;
|
import org.keycloak.models.workflow.RestartWorkflowStepProviderFactory;
|
||||||
import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory;
|
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.UserAttributeWorkflowConditionFactory;
|
import org.keycloak.models.workflow.conditions.UserAttributeWorkflowConditionFactory;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
||||||
@@ -21,11 +24,13 @@ import org.keycloak.representations.workflows.WorkflowRepresentation;
|
|||||||
import org.keycloak.representations.workflows.WorkflowStepRepresentation;
|
import org.keycloak.representations.workflows.WorkflowStepRepresentation;
|
||||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||||
|
import org.keycloak.testframework.remote.providers.runonserver.RunOnServer;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@@ -71,6 +76,41 @@ public class UserAttributeWorkflowConditionTest extends AbstractWorkflowTest {
|
|||||||
assertUserAttribute("user-4", true, values);
|
assertUserAttribute("user-4", true, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActivateWorkflowForEligibleResources() {
|
||||||
|
// create some users with attributes
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
try (Response response = managedRealm.admin().users().create(UserConfigBuilder.create().username("user-with-attr-" + i)
|
||||||
|
.attribute("key", "value").build())) {
|
||||||
|
assertThat(response.getStatus(), is(Status.CREATED.getStatusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
// activate the workflow for all eligible users
|
||||||
|
provider.activateForAllEligibleResources(registeredWorkflows.get(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
runOnServer.run((RunOnServer) session -> {
|
||||||
|
// check the same users are now scheduled to run the second step.
|
||||||
|
WorkflowProvider provider = session.getProvider(WorkflowProvider.class);
|
||||||
|
List<Workflow> registeredWorkflows = provider.getWorkflows().toList();
|
||||||
|
assertThat(registeredWorkflows, hasSize(1));
|
||||||
|
Workflow workflow = registeredWorkflows.get(0);
|
||||||
|
// check workflow was correctly assigned to the users
|
||||||
|
WorkflowStateProvider stateProvider = session.getProvider(WorkflowStateProvider.class);
|
||||||
|
List<WorkflowStateProvider.ScheduledStep> scheduledSteps = stateProvider.getScheduledStepsByWorkflow(workflow);
|
||||||
|
assertThat(scheduledSteps, hasSize(10));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void assertUserAttribute(String username, boolean shouldExist, String... values) {
|
private void assertUserAttribute(String username, boolean shouldExist, String... values) {
|
||||||
assertUserAttribute(username, shouldExist, Map.of("attribute", List.of(values)));
|
assertUserAttribute(username, shouldExist, Map.of("attribute", List.of(values)));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user