From be65ba86892b55a395b4cb39a72f78f2f991a703 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 31 Oct 2023 11:45:28 -0300 Subject: [PATCH] Make sure optional default attributes are removed when decorating the user-define user profile configuration Closes #24420 --- .../DeclarativeUserProfileProvider.java | 15 ++++++- .../user/profile/UserProfileTest.java | 40 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java index 211c6d8972..8d17a2267e 100644 --- a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java +++ b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java @@ -27,6 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -448,6 +449,10 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider< return UserModel.USERNAME.equals(attributeName) || UserModel.EMAIL.equals(attributeName); } + private boolean isOptionalBuiltInAttribute(String attributeName) { + return UserModel.FIRST_NAME.equals(attributeName) || UserModel.LAST_NAME.equals(attributeName); + } + private Predicate createViewAllowedPredicate(Predicate canEdit, Set viewRoles) { return ac -> UPConfigUtils.isRoleForContext(ac.getContext(), viewRoles) || canEdit.test(ac); @@ -521,7 +526,11 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider< throw new RuntimeException("UserProfile configuration for realm '" + session.getContext().getRealm().getName() + "' is invalid: " + errors.toString()); } - for (AttributeMetadata metadata : decoratedMetadata.getAttributes()) { + Iterator attributes = decoratedMetadata.getAttributes().iterator(); + + while (attributes.hasNext()) { + AttributeMetadata metadata = attributes.next(); + String attributeName = metadata.getName(); if (isBuiltInAttribute(attributeName)) { @@ -534,6 +543,10 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider< // user-defined configuration will add its own validators validators.removeIf(m -> m.getValidatorId().equals(id)); } + } else if (isOptionalBuiltInAttribute(attributeName)) { + // removes optional default attributes in favor of user-defined configuration + // make sure any attribute other than username and email are removed from the metadata + attributes.remove(); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/UserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/UserProfileTest.java index c8f4744925..e1df4317fa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/UserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/UserProfileTest.java @@ -1665,4 +1665,44 @@ public class UserProfileTest extends AbstractUserProfileTest { assertNull(user.getEmail()); assertEquals(upAttributes.getFirstValue(UserModel.FIRST_NAME), attributes.get(UserModel.FIRST_NAME).get(0)); } + + @Test + public void testRemoveOptionalAttributesFromDefaultConfigIfNotSet() { + getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testRemoveOptionalAttributesFromDefaultConfigIfNotSet); + } + + private static void testRemoveOptionalAttributesFromDefaultConfigIfNotSet(KeycloakSession session) throws IOException { + UPConfig config = new UPConfig(); + UPAttribute attribute = new UPAttribute(); + + attribute.setName("foo"); + + config.addAttribute(attribute); + + UserProfileProvider provider = getUserProfileProvider(session); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); + + Map attributes = new HashMap<>(); + + attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId() + "@keycloak.org"); + attributes.put(UserModel.EMAIL, org.keycloak.models.utils.KeycloakModelUtils.generateId() + "@keycloak.org"); + attributes.put("foo", "foo"); + + UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes); + UserModel user = profile.create(); + + assertFalse(profile.getAttributes().contains(UserModel.FIRST_NAME)); + assertFalse(profile.getAttributes().contains(UserModel.LAST_NAME)); + + UPAttribute firstName = new UPAttribute(); + firstName.setName(UserModel.FIRST_NAME); + config.addAttribute(firstName); + UPAttribute lastName = new UPAttribute(); + lastName.setName(UserModel.LAST_NAME); + config.addAttribute(lastName); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); + profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes, user); + assertTrue(profile.getAttributes().contains(UserModel.FIRST_NAME)); + assertTrue(profile.getAttributes().contains(UserModel.LAST_NAME)); + } }