Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ protected Set<String> getModelAttributeNames() {
continue;
}

String scimName = (String) metadata.getAnnotations().get(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE);
Map<String, Object> annotations = metadata.getAnnotations();

if (annotations == null) {
// user profile attributes are not required to declare annotations
continue;
}

String scimName = (String) annotations.get(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE);

if (scimName == null || !scimName.contains(":")) {
continue;
Expand Down Expand Up @@ -181,13 +188,14 @@ private Attribute<UserModel, User> createCustomAttribute(Object scimName, boolea
AttributeMetadata metadata = attributes.getMetadata(modelName);

if (metadata == null) {
return null;
continue;
}

Map<String, Object> annotations = metadata.getAnnotations();

if (annotations == null) {
return null;
// skip attributes without annotations instead of aborting the lookup
continue;
}

Object modelScimAttributeName = annotations.get(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,33 @@ public void testFilterConjunctionAcrossDifferentCustomAttributes() {
assertSingleResult(client.users().getAll(filter), user.getUserName());
}

@Test
public void testFilterCustomAttributeWithUnannotatedUserProfileAttribute() {
// user profile attributes are not required to declare annotations. An attribute without any
// annotations used to cause a NullPointerException when resolving the custom SCIM schema
// attributes, and aborted the model attribute lookup for any attribute declared after it.
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
UPAttribute unannotated = new UPAttribute("unannotated");
unannotated.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(unannotated);
realm.admin().users().userProfile().update(upConfig);
assertThat(realm.admin().users().userProfile().getConfiguration().getAttribute("unannotated").getAnnotations(), is(nullValue()));

String customSchema = "urn:test:custom:" + KeycloakModelUtils.generateId();
addOrReplaceUPAttribute(customSchema, "attrOne");

User user = createUser("bob");
UserRepresentation rep = realm.admin().users().get(user.getId()).toRepresentation();
rep.setAttributes(Map.of(
"unannotated", List.of("some-value"),
"scim.attrOne", List.of("value-one")));
realm.admin().users().get(user.getId()).update(rep);
createUser("alice");

String filter = ResourceFilter.filter().eq(customSchema + ":attrOne", "value-one").build();
assertSingleResult(client.users().getAll(filter), user.getUserName());
}

@Test
public void testFilterMultivaluedCustomAttributeConjunctionAndNegation() {
// Regression test for https://github.com/keycloak/keycloak/issues/51805: JOIN-based predicate
Expand Down
Loading