diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttribute.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttribute.java index 3e7ba12bfa..55d57733d4 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttribute.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttribute.java @@ -49,6 +49,26 @@ public class UPAttribute { this.name = name != null ? name.trim() : null; } + public UPAttribute(String name, UPGroup group) { + this(name); + this.group = group.getName(); + } + + public UPAttribute(String name, UPAttributePermissions permissions, UPAttributeRequired required, UPAttributeSelector selector) { + this(name); + this.permissions = permissions; + this.required = required; + this.selector = selector; + } + + public UPAttribute(String name, UPAttributePermissions permissions, UPAttributeRequired required) { + this(name, permissions, required, null); + } + + public UPAttribute(String name, UPAttributePermissions permissions) { + this(name, permissions, null); + } + public String getName() { return name; } diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributePermissions.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributePermissions.java index c04d3c5475..d4b780a8c3 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributePermissions.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributePermissions.java @@ -33,6 +33,15 @@ public class UPAttributePermissions { private Set view = Collections.emptySet(); private Set edit = Collections.emptySet(); + public UPAttributePermissions() { + // for reflection + } + + public UPAttributePermissions(Set view, Set edit) { + this.view = view; + this.edit = edit; + } + public Set getView() { return view; } diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeRequired.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeRequired.java index 51e7eea9f5..f93628a559 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeRequired.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeRequired.java @@ -33,6 +33,15 @@ public class UPAttributeRequired { private Set roles; private Set scopes; + public UPAttributeRequired() { + // for reflection + } + + public UPAttributeRequired(Set roles, Set scopes) { + this.roles = roles; + this.scopes = scopes; + } + /** * Check if this config means that the attribute is ALWAYS required. * diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeSelector.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeSelector.java index 925ff0618d..5fa162eea4 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeSelector.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPAttributeSelector.java @@ -30,6 +30,14 @@ public class UPAttributeSelector { private Set scopes; + public UPAttributeSelector() { + // for reflection + } + + public UPAttributeSelector(Set scopes) { + this.scopes = scopes; + } + public Set getScopes() { return scopes; } diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPConfig.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPConfig.java index 29cbb448c7..86a9af8f75 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPConfig.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPConfig.java @@ -50,16 +50,21 @@ public class UPConfig { this.attributes = attributes; } - public UPConfig addAttribute(UPAttribute attribute) { + public UPConfig addOrReplaceAttribute(UPAttribute attribute) { if (attributes == null) { attributes = new ArrayList<>(); } + removeAttribute(attribute.getName()); attributes.add(attribute); return this; } + public boolean removeAttribute(String name) { + return attributes != null && attributes.removeIf(attribute -> attribute.getName().equals(name)); + } + public List getGroups() { if (groups == null) { return Collections.emptyList(); diff --git a/core/src/main/java/org/keycloak/representations/userprofile/config/UPGroup.java b/core/src/main/java/org/keycloak/representations/userprofile/config/UPGroup.java index 124427309a..9d69f6fea6 100644 --- a/core/src/main/java/org/keycloak/representations/userprofile/config/UPGroup.java +++ b/core/src/main/java/org/keycloak/representations/userprofile/config/UPGroup.java @@ -33,6 +33,14 @@ public class UPGroup { private String displayDescription; private Map annotations; + public UPGroup() { + // for reflection + } + + public UPGroup(String name) { + this.name = name; + } + public String getName() { return name; } diff --git a/js/apps/admin-ui/cypress/e2e/realm_settings_user_profile_enabled.spec.ts b/js/apps/admin-ui/cypress/e2e/realm_settings_user_profile_enabled.spec.ts index 74781cca3f..ce3bf4b12d 100644 --- a/js/apps/admin-ui/cypress/e2e/realm_settings_user_profile_enabled.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/realm_settings_user_profile_enabled.spec.ts @@ -131,6 +131,8 @@ describe("User profile tabs", () => { { "attributes": [ { + "name": "email"{downArrow}, + { "name": "username", "validations": { "length": { diff --git a/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx b/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx index 64ffb1aaf3..3c2b66d143 100644 --- a/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx @@ -191,6 +191,7 @@ export const AttributesTab = () => { { title: t("delete"), isActionable: ({ name }) => !RESTRICTED_ATTRIBUTES.includes(name!), + isDisabled: RESTRICTED_ATTRIBUTES.includes(name!), onClick: (_key, _idx, component) => { setAttributeToDelete(component.name); toggleDeleteDialog(); diff --git a/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java b/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java index aeb76368e8..92b01afef7 100644 --- a/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java +++ b/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -36,6 +37,7 @@ import org.keycloak.common.util.StreamUtil; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; import org.keycloak.representations.userprofile.config.UPAttribute; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.userprofile.UserProfileContext; @@ -128,6 +130,7 @@ public class UPConfigUtils { if (config.getAttributes() != null) { Set attNamesCache = new HashSet<>(); config.getAttributes().forEach((attribute) -> validateAttribute(session, attribute, groups, errors, attNamesCache)); + errors.addAll(validateRootAttributes(config)); } else { errors.add("UserProfile configuration without 'attributes' section is not allowed"); } @@ -135,6 +138,25 @@ public class UPConfigUtils { return errors; } + private static List validateRootAttributes(UPConfig config) { + List attributes = config.getAttributes(); + + if (attributes == null) { + return Collections.emptyList(); + } + + List errors = new ArrayList<>(); + List attributeNames = attributes.stream().map(UPAttribute::getName).collect(Collectors.toList()); + + for (String name : Arrays.asList(UserModel.USERNAME, UserModel.EMAIL)) { + if (!attributeNames.contains(name)) { + errors.add("The attribute '" + name + "' can not be removed"); + } + } + + return errors; + } + /** * Validate attribute configuration * @@ -179,7 +201,7 @@ public class UPConfigUtils { if (attributeConfig.getGroup() != null) { if (!groups.contains(attributeConfig.getGroup())) { - errors.add("Attribute '" + attributeName + "' references unknown group '" + attributeConfig.getGroup() + "'"); + errors.add("Attribute '" + attributeName + "' references unknown group '" + attributeConfig.getGroup() + "'"); } } @@ -304,4 +326,12 @@ public class UPConfigUtils { throw new RuntimeException("Failed to load default user profile config file", cause); } } + + public static UPConfig parseDefaultConfig() { + try { + return JsonSerialization.readValue(readDefaultConfig(), UPConfig.class); + } catch (IOException e) { + throw new RuntimeException("Failed to parse default user profile configuration", e); + } + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java index d8195da70c..2570bca8c7 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java @@ -61,7 +61,7 @@ public class UserTestWithUserProfile extends UserTest { UPConfig upConfig = realm.users().userProfile().getConfiguration(); for (String name : managedAttributes) { - upConfig.addAttribute(createAttributeMetadata(name)); + upConfig.addOrReplaceAttribute(createAttributeMetadata(name)); } VerifyProfileTest.setUserProfileConfiguration(realm, JsonSerialization.writeValueAsString(upConfig)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/userprofile/UserProfileAdminTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/userprofile/UserProfileAdminTest.java index 01c705e8cb..8d83ef890b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/userprofile/UserProfileAdminTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/userprofile/UserProfileAdminTest.java @@ -44,6 +44,7 @@ import org.keycloak.representations.userprofile.config.UPAttribute; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPGroup; import org.keycloak.testsuite.util.JsonTestUtils; +import org.keycloak.userprofile.config.UPConfigUtils; /** * @author Pedro Igor @@ -66,7 +67,7 @@ public class UserProfileAdminTest extends AbstractAdminTest { @Test public void testSetDefaultConfig() { - UPConfig config = new UPConfig().addAttribute(new UPAttribute("test")); + UPConfig config = UPConfigUtils.parseDefaultConfig().addOrReplaceAttribute(new UPAttribute("test")); UserProfileResource userProfile = testRealm().users().userProfile(); userProfile.update(config); getCleanup().addCleanup(() -> testRealm().users().userProfile().update(null)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java index fdeb52ddeb..d0e4839084 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -47,6 +47,7 @@ import org.keycloak.testsuite.runonserver.RunHelpers; import org.keycloak.testsuite.util.JsonTestUtils; import org.keycloak.testsuite.util.UserBuilder; import org.keycloak.userprofile.DeclarativeUserProfileProvider; +import org.keycloak.util.JsonSerialization; import java.io.File; import java.io.IOException; @@ -273,8 +274,8 @@ public class ExportImportTest extends AbstractKeycloakTest { realmRes.update(realmRep); //add some non-default config - VerifyProfileTest.setUserProfileConfiguration(realmRes, VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT); - + UPConfig persistedConfig = VerifyProfileTest.setUserProfileConfiguration(realmRes, VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT); + //export TestingExportImportResource exportImport = testingClient.testing().exportImport(); exportImport.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); @@ -297,7 +298,7 @@ public class ExportImportTest extends AbstractKeycloakTest { MultivaluedHashMap config = userProfileComponents.get(0).getConfig(); assertThat(config, notNullValue()); assertThat(config.size(), equalTo(1)); - JsonTestUtils.assertJsonEquals(config.getFirst(DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY), VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT, UPConfig.class); + JsonTestUtils.assertJsonEquals(config.getFirst(DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY), JsonSerialization.writeValueAsString(persistedConfig), UPConfig.class); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiWithUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiWithUserProfileTest.java index b7275b1451..00779ebd38 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiWithUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiWithUserProfileTest.java @@ -108,7 +108,7 @@ public class LDAPAdminRestApiWithUserProfileTest extends LDAPAdminRestApiTest { attribute.setPermissions(permissions); - upConfig.addAttribute(attribute); + upConfig.addOrReplaceAttribute(attribute); setUserProfileConfiguration(testRealm(), writeValueAsString(upConfig)); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java index 7ced537d6e..67e57045f0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java @@ -22,12 +22,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED; +import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN; +import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.StringUtils; @@ -46,6 +49,9 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.userprofile.config.UPAttribute; +import org.keycloak.representations.userprofile.config.UPAttributePermissions; +import org.keycloak.representations.userprofile.config.UPAttributeRequired; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AssertEvents; @@ -1159,7 +1165,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest { + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", " + VALIDATIONS_LENGTH + "}" + "]}"; - setUserProfileConfiguration(customConfig); + UPConfig persistedConfig = setUserProfileConfiguration(customConfig); RealmResource realmRes = testRealm(); disableDynamicUserProfile(realmRes, false); @@ -1167,7 +1173,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest { enableDynamicUserProfile(realm); testRealm().update(realm); - JsonTestUtils.assertJsonEquals(customConfig, realmRes.users().userProfile().getConfiguration()); + JsonTestUtils.assertJsonEquals(JsonSerialization.writeValueAsString(persistedConfig), realmRes.users().userProfile().getConfiguration()); } protected UserRepresentation getUser(String userId) { @@ -1186,8 +1192,8 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest { testRealm().users().get(userId).update(ur); } - protected void setUserProfileConfiguration(String configuration) { - setUserProfileConfiguration(testRealm(), configuration); + protected UPConfig setUserProfileConfiguration(String configuration) { + return setUserProfileConfiguration(testRealm(), configuration); } public static void enableDynamicUserProfile(RealmRepresentation testRealm) { @@ -1214,10 +1220,27 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest { } - public static void setUserProfileConfiguration(RealmResource testRealm, String configuration) { + public static UPConfig setUserProfileConfiguration(RealmResource testRealm, String configuration) { try { UPConfig config = configuration == null ? null : JsonSerialization.readValue(configuration, UPConfig.class); + + if (config != null) { + UPAttribute username = config.getAttribute(UserModel.USERNAME); + + if (username == null) { + config.addOrReplaceAttribute(new UPAttribute(UserModel.USERNAME)); + } + + UPAttribute email = config.getAttribute(UserModel.EMAIL); + + if (email == null) { + config.addOrReplaceAttribute(new UPAttribute(UserModel.EMAIL, new UPAttributePermissions(Set.of(ROLE_USER, ROLE_ADMIN), Set.of(ROLE_USER, ROLE_ADMIN)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of()))); + } + } + testRealm.users().userProfile().update(config); + + return config; } catch (IOException ioe) { throw new RuntimeException("Failed to read configuration", ioe); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/AbstractUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/AbstractUserProfileTest.java index 50c164d963..aacd861bd6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/AbstractUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/AbstractUserProfileTest.java @@ -104,7 +104,7 @@ public abstract class AbstractUserProfileTest extends AbstractTestRealmKeycloakT Map validatorConfig = new HashMap<>(); validatorConfig.put("min", 3); attribute.addValidation("length", validatorConfig); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); } String newConfig = JsonSerialization.writeValueAsString(config); return newConfig; 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 ccaa130c72..fb56277ae6 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 @@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN; import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER; +import static org.keycloak.userprofile.config.UPConfigUtils.parseDefaultConfig; import jakarta.ws.rs.core.Response; import java.io.IOException; @@ -58,6 +59,7 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; +import org.keycloak.representations.userprofile.config.UPGroup; import org.keycloak.services.messages.Messages; import org.keycloak.testsuite.runonserver.RunOnServer; import org.keycloak.testsuite.util.LDAPRule; @@ -75,7 +77,6 @@ import org.keycloak.userprofile.UserProfile; import org.keycloak.userprofile.UserProfileContext; import org.keycloak.userprofile.UserProfileProvider; import org.keycloak.userprofile.ValidationException; -import org.keycloak.userprofile.config.UPConfigUtils; import org.keycloak.userprofile.validator.UsernameIDNHomographValidator; import org.keycloak.util.JsonSerialization; import org.keycloak.validate.ValidationError; @@ -111,7 +112,8 @@ public class UserProfileTest extends AbstractUserProfileTest { @Test public void testReadOnlyAllowed() throws Exception { // create a user with attribute foo value 123 allowed by the profile now but disallowed later - UPConfig config = JsonSerialization.readValue("{\"attributes\": [{\"name\": \"foo\", \"permissions\": {\"edit\": [\"admin\"]}}]}", UPConfig.class); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("foo", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); RealmResource realmRes = testRealm(); realmRes.users().userProfile().update(config); @@ -131,8 +133,10 @@ public class UserProfileTest extends AbstractUserProfileTest { RealmModel realm = session.getContext().getRealm(); UserModel user = session.users().getUserById(realm, userId); UserProfileProvider provider = getUserProfileProvider(session); - provider.setConfiguration("{\"attributes\": [{\"name\": \"foo\", \"validations\": {\"length\": {\"min\": \"5\", \"max\": \"15\"}}, \"permissions\": {\"edit\": [\"admin\"]}}]}"); - + UPConfig upConfig = provider.getConfiguration(); + upConfig.getAttribute("foo") + .setValidations(Map.of("length", Map.of("min", "5", "max", "15"))); + provider.setConfiguration(JsonSerialization.writeValueAsString(upConfig)); Map> attributes = new HashMap<>(user.getAttributes()); UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes, user); profile.validate(); @@ -143,7 +147,11 @@ public class UserProfileTest extends AbstractUserProfileTest { RealmModel realm = session.getContext().getRealm(); UserModel user = session.users().getUserById(realm, userId); UserProfileProvider provider = getUserProfileProvider(session); - provider.setConfiguration("{\"attributes\": [{\"name\": \"foo\", \"validations\": {\"length\": {\"min\": \"5\", \"max\": \"15\"}}, \"permissions\": {\"edit\": [\"admin\", \"user\"]}}]}"); + UPConfig upConfig = provider.getConfiguration(); + UPAttribute changedFoo = upConfig.getAttribute("foo"); + changedFoo.setPermissions(new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN))); + changedFoo.setValidations(Map.of("length", Map.of("min", "5", "max", "15"))); + provider.setConfiguration(JsonSerialization.writeValueAsString(upConfig)); Map> attributes = new HashMap<>(user.getAttributes()); UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes, user); @@ -174,7 +182,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testCustomAttributeInAnyContext); } - private static void testCustomAttributeInAnyContext(KeycloakSession session) { + private static void testCustomAttributeInAnyContext(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, "profiled-user"); @@ -183,8 +191,9 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put(UserModel.EMAIL, org.keycloak.models.utils.KeycloakModelUtils.generateId() + "@keycloak.org"); UserProfileProvider provider = getUserProfileProvider(session); - - provider.setConfiguration("{\"attributes\": [{\"name\": \"address\", \"required\": {}, \"permissions\": {\"edit\": [\"user\"]}}]}"); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired())); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes); @@ -198,10 +207,12 @@ public class UserProfileTest extends AbstractUserProfileTest { containsInAnyOrder(UserModel.USERNAME, UserModel.EMAIL, UserModel.FIRST_NAME, UserModel.LAST_NAME, "address"); + // not writable in user api, no validation should happen + profile = provider.create(UserProfileContext.USER_API, attributes); + profile.validate(); + attributes.put("address", "myaddress"); - profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes); - profile.validate(); } @@ -210,7 +221,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testResolveProfile); } - private static void testResolveProfile(KeycloakSession session) { + private static void testResolveProfile(KeycloakSession session) throws IOException { configureAuthenticationSession(session); Map attributes = new HashMap<>(); @@ -221,8 +232,9 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put(UserModel.EMAIL, org.keycloak.models.utils.KeycloakModelUtils.generateId() + "@keycloak.org"); UserProfileProvider provider = getUserProfileProvider(session); - - provider.setConfiguration("{\"attributes\": [{\"name\": \"business.address\", \"required\": {\"scopes\": [\"customer\"]}, \"permissions\": {\"edit\": [\"user\"]}}]}"); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("business.address", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(), Set.of("customer")))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes); @@ -346,21 +358,8 @@ public class UserProfileTest extends AbstractUserProfileTest { UserModel user = session.users().addUser(realm, "profiled-user"); UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName("address"); - - UPAttributeRequired requirements = new UPAttributeRequired(); - - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(ROLE_USER)); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired())); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, user); @@ -388,15 +387,17 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testGetProfileAttributes); } - private static void testGetProfileAttributes(KeycloakSession session) { + private static void testGetProfileAttributes(KeycloakSession session) throws IOException { RealmModel realm = session.getContext().getRealm(); UserModel user = session.users().addUser(realm, org.keycloak.models.utils.KeycloakModelUtils.generateId()); user.setFirstName("John"); user.setLastName("John"); user.setEmail(org.keycloak.models.utils.KeycloakModelUtils.generateId() + "@keycloak.org"); - UserProfileProvider provider = getUserProfileProvider(session); - provider.setConfiguration("{\"attributes\": [{\"name\": \"address\", \"required\": {}, \"permissions\": {\"edit\": [\"user\"]}}]}"); + UserProfileProvider provider = getUserProfileProvider(session); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired())); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, user); Attributes attributes = profile.getAttributes(); @@ -433,38 +434,21 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testGetProfileAttributeGroups); } - private static void testGetProfileAttributeGroups(KeycloakSession session) { + private static void testGetProfileAttributeGroups(KeycloakSession session) throws IOException { RealmModel realm = session.getContext().getRealm(); UserModel user = session.users().addUser(realm, org.keycloak.models.utils.KeycloakModelUtils.generateId()); UserProfileProvider provider = getUserProfileProvider(session); - - String configuration = "{\n" + - " \"attributes\": [\n" + - " {\n" + - " \"name\": \"address\",\n" + - " \"group\": \"companyaddress\"\n" + - " },\n" + - " {\n" + - " \"name\": \"second\",\n" + - " \"group\": \"groupwithanno" + "\"\n" + - " }\n" + - " ],\n" + - " \"groups\": [\n" + - " {\n" + - " \"name\": \"companyaddress\",\n" + - " \"displayHeader\": \"header\",\n" + - " \"displayDescription\": \"description\"\n" + - " },\n" + - " {\n" + - " \"name\": \"groupwithanno\",\n" + - " \"annotations\": {\n" + - " \"anno1\": \"value1\",\n" + - " \"anno2\": \"value2\"\n" + - " }\n" + - " }\n" + - " ]\n" + - "}\n"; - provider.setConfiguration(configuration); + UPConfig config = parseDefaultConfig(); + UPGroup companyAddress = new UPGroup("companyaddress"); + companyAddress.setDisplayHeader("header"); + companyAddress.setDisplayDescription("description"); + config.addGroup(companyAddress); + config.addOrReplaceAttribute(new UPAttribute("address", companyAddress)); + UPGroup groupWithAnnotation = new UPGroup("groupwithanno"); + groupWithAnnotation.setAnnotations(Map.of("anno1", "value1", "anno2", "value2")); + config.addGroup(groupWithAnnotation); + config.addOrReplaceAttribute(new UPAttribute("second", groupWithAnnotation)); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, user); Attributes attributes = profile.getAttributes(); @@ -498,20 +482,8 @@ public class UserProfileTest extends AbstractUserProfileTest { UserProfileProvider provider = getUserProfileProvider(session); UPConfig config = provider.getConfiguration(); - UPAttribute attribute = new UPAttribute(); - attribute.setName("address"); - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(new HashSet<>(Arrays.asList("admin", "user"))); - attribute.setPermissions(permissions); - config.addAttribute(attribute); - - attribute = new UPAttribute(); - attribute.setName("business.address"); - permissions = new UPAttributePermissions(); - permissions.setEdit(new HashSet<>(Arrays.asList("admin", "user"))); - attribute.setPermissions(permissions); - config.addAttribute(attribute); - + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); + config.addOrReplaceAttribute(new UPAttribute("business.address", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -572,7 +544,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testReadonlyUpdates); } - private static void testReadonlyUpdates(KeycloakSession session) { + private static void testReadonlyUpdates(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId()); @@ -583,8 +555,9 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put("department", Arrays.asList("sales")); UserProfileProvider provider = getUserProfileProvider(session); - - provider.setConfiguration("{\"attributes\": [{\"name\": \"department\", \"permissions\": {\"edit\": [\"admin\"]}}]}"); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("department", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes); UserModel user = profile.create(); @@ -625,7 +598,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testReadonlyEmailCannotBeUpdated); } - private static void testReadonlyEmailCannotBeUpdated(KeycloakSession session) { + private static void testReadonlyEmailCannotBeUpdated(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId()); @@ -634,9 +607,11 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put(UserModel.EMAIL, "readonly@foo.bar"); UserProfileProvider provider = getUserProfileProvider(session); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("email", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); // configure email r/o for user - provider.setConfiguration("{\"attributes\": [{\"name\": \"email\", \"permissions\": {\"edit\": [ \"admin\"]}}]}"); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes); UserModel user = profile.create(); @@ -653,15 +628,18 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put(UserModel.EMAIL, "cannot-change@foo.bar"); profile = provider.create(UserProfileContext.ACCOUNT, attributes, user); - try { profile.update(); fail("Should fail since email is read only"); } catch (ValidationException ve) { assertTrue(ve.isAttributeOnError("email")); } - assertEquals("E-Mail address shouldn't be changed", "readonly@foo.bar", user.getEmail()); + + attributes.put(UserModel.EMAIL, "admin-can-change@foo.bar"); + profile = provider.create(UserProfileContext.USER_API, attributes, user); + profile.update(); + assertEquals("admin-can-change@foo.bar", user.getEmail()); } @Test @@ -669,7 +647,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testUpdateEmail); } - private static void testUpdateEmail(KeycloakSession session) { + private static void testUpdateEmail(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId()); @@ -678,9 +656,12 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put(UserModel.EMAIL, "canchange@foo.bar"); UserProfileProvider provider = getUserProfileProvider(session); + UPConfig config = parseDefaultConfig(); + + config.getAttribute("email").getPermissions().setEdit(Set.of(ROLE_USER, ROLE_ADMIN)); // configure email r/w for user - provider.setConfiguration("{\"attributes\": [{\"name\": \"email\", \"permissions\": {\"edit\": [ \"user\", \"admin\"]}}]}"); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes); UserModel user = profile.create(); @@ -708,7 +689,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testDoNotUpdateUndefinedAttributes); } - private static void testDoNotUpdateUndefinedAttributes(KeycloakSession session) { + private static void testDoNotUpdateUndefinedAttributes(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId()); @@ -720,10 +701,12 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put("phone", Arrays.asList("fixed-phone")); UserProfileProvider provider = getUserProfileProvider(session); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("department", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); + config.addOrReplaceAttribute(new UPAttribute("phone", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); - provider.setConfiguration("{\"attributes\": [{\"name\": \"department\", \"permissions\": {\"edit\": [\"admin\"]}}," - + "{\"name\": \"phone\", \"permissions\": {\"edit\": [\"admin\"]}}," - + "{\"name\": \"address\", \"permissions\": {\"edit\": [\"admin\"]}}]}"); UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes); UserModel user = profile.create(); assertThat(profile.getAttributes().nameSet(), @@ -737,8 +720,8 @@ public class UserProfileTest extends AbstractUserProfileTest { profile.update((attributeName, userModel, oldValue) -> assertTrue(attributesUpdated.add(attributeName))); assertThat(attributesUpdated, containsInAnyOrder("department", "address", "phone")); - provider.setConfiguration("{\"attributes\": [{\"name\": \"department\", \"permissions\": {\"edit\": [\"admin\"]}}," - + "{\"name\": \"phone\", \"permissions\": {\"edit\": [\"admin\"]}}]}"); + config.removeAttribute("address"); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); attributesUpdated.clear(); attributes.remove("address"); attributes.put("department", "foo"); @@ -748,9 +731,8 @@ public class UserProfileTest extends AbstractUserProfileTest { assertThat(attributesUpdated, containsInAnyOrder("department", "phone")); assertTrue(user.getAttributes().containsKey("address")); - provider.setConfiguration("{\"attributes\": [{\"name\": \"department\", \"permissions\": {\"edit\": [\"admin\"]}}," - + "{\"name\": \"phone\", \"permissions\": {\"edit\": [\"admin\"]}}," - + "{\"name\": \"address\", \"permissions\": {\"edit\": [\"admin\"]}}]}"); + config.addOrReplaceAttribute(new UPAttribute("address", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); attributes.put("department", "foo"); attributes.put("phone", "foo"); attributes.put("address", "bar"); @@ -875,10 +857,8 @@ public class UserProfileTest extends AbstractUserProfileTest { } private static void testCustomValidationForUsername(KeycloakSession session) throws IOException { - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(UserModel.USERNAME); + UPConfig config = parseDefaultConfig(); + UPAttribute attribute = new UPAttribute(UserModel.USERNAME); Map validatorConfig = new HashMap<>(); @@ -886,7 +866,7 @@ public class UserProfileTest extends AbstractUserProfileTest { attribute.addValidation(LengthValidator.ID, validatorConfig); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); UserProfileProvider provider = getUserProfileProvider(session); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -916,7 +896,7 @@ public class UserProfileTest extends AbstractUserProfileTest { provider.setConfiguration(null); - attributes.put(UserModel.USERNAME, "user"); + attributes.put(UserModel.USERNAME, ROLE_USER); attributes.put(UserModel.EMAIL, "user@keycloak.org"); attributes.put(UserModel.FIRST_NAME, "Joe"); attributes.put(UserModel.LAST_NAME, "Doe"); @@ -976,18 +956,18 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testOptionalAttributes(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); + UPConfig config = parseDefaultConfig(); UPAttribute attribute = new UPAttribute(); attribute.setName(UserModel.FIRST_NAME); Map validatorConfig = new HashMap<>(); validatorConfig.put(LengthValidator.KEY_MAX, 4); attribute.addValidation(LengthValidator.ID, validatorConfig); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); attribute = new UPAttribute(); attribute.setName(UserModel.LAST_NAME); attribute.addValidation(LengthValidator.ID, validatorConfig); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -1034,7 +1014,7 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testCustomAttributeRequired(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); + UPConfig config = parseDefaultConfig(); UPAttribute attribute = new UPAttribute(); attribute.setName(ATT_ADDRESS); @@ -1053,7 +1033,7 @@ public class UserProfileTest extends AbstractUserProfileTest { permissions.setEdit(Collections.singleton(ROLE_USER)); attribute.setPermissions(permissions); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -1100,7 +1080,7 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testCustomAttributeOptional(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); + UPConfig config = parseDefaultConfig(); UPAttribute attribute = new UPAttribute(); attribute.setName(ATT_ADDRESS); @@ -1109,7 +1089,7 @@ public class UserProfileTest extends AbstractUserProfileTest { validatorConfig.put(LengthValidator.KEY_MIN, 4); attribute.addValidation(LengthValidator.ID, validatorConfig); - config.addAttribute(attribute); + config.addOrReplaceAttribute(attribute); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -1152,23 +1132,8 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testRequiredIfUser(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - - requirements.setRoles(Collections.singleton(ROLE_USER)); - - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(ROLE_USER)); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of()))); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1215,23 +1180,8 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testRequiredIfAdmin(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - - requirements.setRoles(Collections.singleton(ROLE_ADMIN)); - - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_ADMIN)); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)), new UPAttributeRequired(Set.of(ROLE_ADMIN), Set.of()))); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1269,20 +1219,8 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testNoValidationsIfUserReadOnly(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_ADMIN)); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN)), new UPAttributeRequired())); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1314,20 +1252,8 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testNoValidationsIfAdminReadOnly(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_USER)); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired())); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1355,29 +1281,9 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testIgnoreReadOnlyAttribute(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute firstName = new UPAttribute(); - - firstName.setName(UserModel.FIRST_NAME); - - UPAttribute address = new UPAttribute(); - - address.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - requirements.setRoles(Collections.singleton(UPConfigUtils.ROLE_USER)); - address.setRequired(requirements); - firstName.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_USER)); - permissions.setView(Collections.singleton(ROLE_ADMIN)); - address.setPermissions(permissions); - firstName.setPermissions(permissions); - - config.addAttribute(address); - config.addAttribute(firstName); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of()))); + config.addOrReplaceAttribute(new UPAttribute(UserModel.FIRST_NAME, new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of()))); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1451,23 +1357,8 @@ public class UserProfileTest extends AbstractUserProfileTest { private static void testRequiredByClientScope(KeycloakSession session) throws IOException { UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - - requirements.setScopes(Collections.singleton("client-a")); - - attribute.setRequired(requirements); - - UPAttributePermissions permissions = new UPAttributePermissions(); - permissions.setEdit(Collections.singleton("user")); - attribute.setPermissions(permissions); - - config.addAttribute(attribute); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(), Set.of("client-a")))); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); Map attributes = new HashMap<>(); @@ -1534,23 +1425,10 @@ public class UserProfileTest extends AbstractUserProfileTest { } private static void testConfigurationInvalidScope(KeycloakSession session) throws IOException { - RealmModel realm = session.getContext().getRealm(); UserProfileProvider provider = getUserProfileProvider(session); - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName(ATT_ADDRESS); - - UPAttributeRequired requirements = new UPAttributeRequired(); - - requirements.setScopes(Collections.singleton("invalid")); - - attribute.setRequired(requirements); - - attribute.setSelector(new UPAttributeSelector()); - attribute.getSelector().setScopes(Collections.singleton("invalid")); - - config.addAttribute(attribute); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute(ATT_ADDRESS, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), + new UPAttributeRequired(Set.of(), Set.of("invalid")), new UPAttributeSelector(Set.of("invalid")))); try { provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -1599,7 +1477,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testDoNotRemoveAttributes); } - private static void testDoNotRemoveAttributes(KeycloakSession session) { + private static void testDoNotRemoveAttributes(KeycloakSession session) throws IOException { Map attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId()); @@ -1608,11 +1486,14 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put("foo", Arrays.asList("foo")); UserProfileProvider provider = getUserProfileProvider(session); + UPConfig config = parseDefaultConfig(); + config.removeAttribute(UserModel.FIRST_NAME); + config.removeAttribute(UserModel.LAST_NAME); + config.addOrReplaceAttribute(new UPAttribute("test-attribute", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN)))); + config.addOrReplaceAttribute(new UPAttribute("foo", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN)))); + config.addOrReplaceAttribute(new UPAttribute("email", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN)))); - provider.setConfiguration("{\"attributes\": [" - + "{\"name\": \"test-attribute\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"foo\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"email\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}]}"); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.USER_API, attributes); UserModel user = profile.create(); @@ -1650,10 +1531,8 @@ public class UserProfileTest extends AbstractUserProfileTest { assertEquals("Test Value", userAttributes.getFirstValue("test-attribute")); assertNull(userAttributes.getFirstValue("foo")); - provider.setConfiguration("{\"attributes\": [" - + "{\"name\": \"test-attribute\", \"permissions\": {\"edit\": [\"user\"]}}," - + "{\"name\": \"foo\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"email\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}]}"); + config.addOrReplaceAttribute(new UPAttribute("test-attribute", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); attributes.remove("test-attribute"); profile = provider.create(UserProfileContext.USER_API, attributes, user); profile.update(true); @@ -1663,10 +1542,8 @@ public class UserProfileTest extends AbstractUserProfileTest { assertEquals("new-email@test.com", userAttributes.getFirstValue(UserModel.EMAIL)); assertEquals("Test Value", userAttributes.getFirstValue("test-attribute")); - provider.setConfiguration("{\"attributes\": [" - + "{\"name\": \"test-attribute\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"foo\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"email\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}]}"); + config.addOrReplaceAttribute(new UPAttribute("test-attribute", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); attributes.remove("test-attribute"); profile = provider.create(UserProfileContext.USER_API, attributes, user); profile.update(true); @@ -1682,7 +1559,7 @@ public class UserProfileTest extends AbstractUserProfileTest { getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testRemoveEmptyRootAttribute); } - private static void testRemoveEmptyRootAttribute(KeycloakSession session) { + private static void testRemoveEmptyRootAttribute(KeycloakSession session) throws IOException { Map> attributes = new HashMap<>(); attributes.put(UserModel.USERNAME, List.of(org.keycloak.models.utils.KeycloakModelUtils.generateId())); @@ -1691,12 +1568,12 @@ public class UserProfileTest extends AbstractUserProfileTest { attributes.put("test-attribute", List.of("")); UserProfileProvider provider = getUserProfileProvider(session); - - provider.setConfiguration("{\"attributes\": [" - + "{\"name\": \"test-attribute\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"firstName\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"lastName\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}," - + "{\"name\": \"email\", \"permissions\": {\"edit\": [\"admin\", \"user\"]}}]}"); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("test-attribute", new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); + config.addOrReplaceAttribute(new UPAttribute(UserModel.FIRST_NAME, new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); + config.addOrReplaceAttribute(new UPAttribute(UserModel.LAST_NAME, new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); + config.addOrReplaceAttribute(new UPAttribute(UserModel.EMAIL, new UPAttributePermissions(Set.of(), Set.of(ROLE_ADMIN, ROLE_USER)))); + provider.setConfiguration(JsonSerialization.writeValueAsString(config)); UserProfile profile = provider.create(UserProfileContext.USER_API, attributes); UserModel user = profile.create(); @@ -1730,12 +1607,10 @@ public class UserProfileTest extends AbstractUserProfileTest { } private static void testRemoveOptionalAttributesFromDefaultConfigIfNotSet(KeycloakSession session) throws IOException { - UPConfig config = new UPConfig(); - UPAttribute attribute = new UPAttribute(); - - attribute.setName("foo"); - - config.addAttribute(attribute); + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("foo")); + config.removeAttribute(UserModel.FIRST_NAME); + config.removeAttribute(UserModel.LAST_NAME); UserProfileProvider provider = getUserProfileProvider(session); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); @@ -1754,10 +1629,10 @@ public class UserProfileTest extends AbstractUserProfileTest { UPAttribute firstName = new UPAttribute(); firstName.setName(UserModel.FIRST_NAME); - config.addAttribute(firstName); + config.addOrReplaceAttribute(firstName); UPAttribute lastName = new UPAttribute(); lastName.setName(UserModel.LAST_NAME); - config.addAttribute(lastName); + config.addOrReplaceAttribute(lastName); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); profile = provider.create(UserProfileContext.UPDATE_PROFILE, attributes, user); assertTrue(profile.getAttributes().contains(UserModel.FIRST_NAME)); @@ -1770,16 +1645,8 @@ public class UserProfileTest extends AbstractUserProfileTest { } private static void testUnmanagedPolicy(KeycloakSession session) throws IOException { - UPConfig config = new UPConfig(); - UPAttribute bar = new UPAttribute("bar"); - UPAttributePermissions permissions = new UPAttributePermissions(); - - permissions.setEdit(Set.of("user", "admin")); - - bar.setPermissions(permissions); - - config.addAttribute(bar); - + UPConfig config = parseDefaultConfig(); + config.addOrReplaceAttribute(new UPAttribute("bar", new UPAttributePermissions(Set.of(), Set.of(ROLE_USER, ROLE_ADMIN)))); UserProfileProvider provider = getUserProfileProvider(session); provider.setConfiguration(JsonSerialization.writeValueAsString(config)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigParserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigParserTest.java index 0210424d9a..a07364f793 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigParserTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigParserTest.java @@ -198,7 +198,7 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest { UPConfig config = loadValidConfig(); //we run this test without KeycloakSession so validator configs are not validated here - UPAttribute attConfig = config.getAttributes().get(1); + UPAttribute attConfig = config.getAttributes().get(2); attConfig.setName(null); List errors = validate(session, config); @@ -209,7 +209,7 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest { Assert.assertEquals(1, errors.size()); // duplicate attribute name - attConfig.setName("firstName"); + attConfig.setName("lastName"); errors = validate(session, config); Assert.assertEquals(1, errors.size());