mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-03 20:39:33 -05:00
Check only for the existence of the attribute if only the key is specified
Closes #45983 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
021d544000
commit
2111dcf913
2 changed files with 42 additions and 13 deletions
|
|
@ -50,8 +50,17 @@ public class UserAttributeWorkflowConditionProvider implements WorkflowCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] parsedKeyValuePair = parseKeyValuePair(expectedAttribute);
|
String[] parsedKeyValuePair = parseKeyValuePair(expectedAttribute);
|
||||||
List<String> values = user.getAttributes().getOrDefault(parsedKeyValuePair[0], List.of());
|
String key = parsedKeyValuePair[0];
|
||||||
List<String> expectedValues = List.of(parsedKeyValuePair[1].split(","));
|
String valuePart = parsedKeyValuePair[1];
|
||||||
|
|
||||||
|
// Presence-only: "key:" -> true if user has at least one attribute with that key
|
||||||
|
if (valuePart.isEmpty()) {
|
||||||
|
List<String> values = user.getAttributes().getOrDefault(key, List.of());
|
||||||
|
return !values.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> values = user.getAttributes().getOrDefault(key, List.of());
|
||||||
|
List<String> expectedValues = List.of(valuePart.split(","));
|
||||||
|
|
||||||
return collectionEquals(expectedValues, values);
|
return collectionEquals(expectedValues, values);
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +71,14 @@ public class UserAttributeWorkflowConditionProvider implements WorkflowCondition
|
||||||
|
|
||||||
String[] parsedKeyValuePair = parseKeyValuePair(expectedAttribute);
|
String[] parsedKeyValuePair = parseKeyValuePair(expectedAttribute);
|
||||||
String attributeName = parsedKeyValuePair[0];
|
String attributeName = parsedKeyValuePair[0];
|
||||||
List<String> expectedValues = Arrays.asList(parsedKeyValuePair[1].split(","));
|
String valuePart = parsedKeyValuePair[1];
|
||||||
|
|
||||||
|
// Presence-only: require at least one attribute with this name for the user
|
||||||
|
if (valuePart.isEmpty()) {
|
||||||
|
return cb.greaterThan(createTotalCountSubquery(cb, query, path, attributeName), 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> expectedValues = Arrays.asList(valuePart.split(","));
|
||||||
|
|
||||||
// Subquery to count how many of the expected values the user has
|
// Subquery to count how many of the expected values the user has
|
||||||
// to check if there is no missing value
|
// to check if there is no missing value
|
||||||
|
|
@ -95,24 +111,29 @@ public class UserAttributeWorkflowConditionProvider implements WorkflowCondition
|
||||||
|
|
||||||
// Subquery to count total attributes with this name for the user
|
// Subquery to count total attributes with this name for the user
|
||||||
// to check if there are no extra values
|
// to check if there are no extra values
|
||||||
Subquery<Long> totalCountSubquery = query.subquery(Long.class);
|
createTotalCountSubquery(cb, query, path, attributeName);
|
||||||
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)
|
// Both counts must equal the expected count (exact match)
|
||||||
int expectedCount = expectedValues.size();
|
int expectedCount = expectedValues.size();
|
||||||
return cb.and(
|
return cb.and(
|
||||||
cb.equal(matchingCountSubquery, expectedCount),
|
cb.equal(matchingCountSubquery, expectedCount),
|
||||||
cb.equal(totalCountSubquery, expectedCount)
|
cb.equal(createTotalCountSubquery(cb, query, path, attributeName), expectedCount)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Subquery<Long> createTotalCountSubquery(CriteriaBuilder cb, CriteriaQuery<String> query, Root<?> path, String attributeName) {
|
||||||
|
Subquery<Long> totalCountSubquery = query.subquery(Long.class);
|
||||||
|
Root<UserAttributeEntity> attrRoot = totalCountSubquery.from(UserAttributeEntity.class);
|
||||||
|
totalCountSubquery.select(cb.count(attrRoot));
|
||||||
|
totalCountSubquery.where(
|
||||||
|
cb.and(
|
||||||
|
cb.equal(attrRoot.get("user").get("id"), path.get("id")),
|
||||||
|
cb.equal(attrRoot.get("name"), attributeName)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return totalCountSubquery;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (expectedAttribute == null) {
|
if (expectedAttribute == null) {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,14 @@ public class UserAttributeWorkflowConditionTest extends AbstractWorkflowTest {
|
||||||
managedRealm.admin().users().userProfile().update(upConfig);
|
managedRealm.admin().users().userProfile().update(upConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditionForAnyValuedAttribute() {
|
||||||
|
createWorkflow(List.of());
|
||||||
|
assertUserAttribute("user-1", true, "singleValue");
|
||||||
|
assertUserAttribute("user-2", true, "v1", "v2", "v3");
|
||||||
|
assertUserAttribute("user-3", false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConditionForSingleValuedAttribute() {
|
public void testConditionForSingleValuedAttribute() {
|
||||||
String expected = "valid";
|
String expected = "valid";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue