diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-23.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-23.0.0.xml
new file mode 100644
index 00000000000..4f35cc440dc
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-23.0.0.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index 74c6e541d59..b568accd2c4 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -78,5 +78,6 @@
+
diff --git a/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo23_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo23_0_0.java
new file mode 100644
index 00000000000..ef642d0858b
--- /dev/null
+++ b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo23_0_0.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.keycloak.migration.migrators;
+
+import java.util.Optional;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.userprofile.UserProfileProvider;
+
+public class MigrateTo23_0_0 implements Migration {
+
+ public static final ModelVersion VERSION = new ModelVersion("23.0.0");
+
+ private static final String USER_PROFILE_ENABLED_PROP = "userProfileEnabled";
+ private static final String UP_PIECES_COUNT_COMPONENT_CONFIG_KEY = "config-pieces-count";
+ private static final String UP_PIECE_COMPONENT_CONFIG_KEY_BASE = "config-piece-";
+ private static final String UP_COMPONENT_CONFIG_KEY = "kc.user.profile.config";
+
+ @Override
+ public void migrate(KeycloakSession session) {
+ session.realms().getRealmsStream().forEach(this::updateUserProfileConfig);
+ }
+
+ @Override
+ public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
+ updateUserProfileConfig(realm);
+ }
+
+ private void updateUserProfileConfig(RealmModel realm) {
+ if (realm.getAttribute(USER_PROFILE_ENABLED_PROP, Boolean.FALSE)) {
+
+ Optional component = realm.getComponentsStream(realm.getId(), UserProfileProvider.class.getName()).findAny();
+ if (component.isPresent()) {
+ ComponentModel userProfileComponent = component.get();
+ int count = userProfileComponent.get(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY, 0);
+ userProfileComponent.getConfig().remove(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY);
+ if (count < 1) return; // default config
+ String configuration;
+ if (count == 1) {
+ configuration = userProfileComponent.get(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + "0");
+ userProfileComponent.getConfig().remove(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + "0");
+ } else {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ String v = userProfileComponent.get(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + i);
+ userProfileComponent.getConfig().remove(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + i);
+ if (v != null) sb.append(v);
+ }
+ configuration = sb.toString();
+ }
+ userProfileComponent.getConfig().putSingle(UP_COMPONENT_CONFIG_KEY, configuration);
+ realm.updateComponent(userProfileComponent);
+ }
+ }
+ }
+
+ @Override
+ public ModelVersion getVersion() {
+ return VERSION;
+ }
+}
diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java
index 8cd8e390973..30833d2d80e 100644
--- a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java
+++ b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java
@@ -36,6 +36,7 @@ import org.keycloak.migration.migrators.MigrateTo1_9_0;
import org.keycloak.migration.migrators.MigrateTo1_9_2;
import org.keycloak.migration.migrators.MigrateTo21_0_0;
import org.keycloak.migration.migrators.MigrateTo22_0_0;
+import org.keycloak.migration.migrators.MigrateTo23_0_0;
import org.keycloak.migration.migrators.MigrateTo2_0_0;
import org.keycloak.migration.migrators.MigrateTo2_1_0;
import org.keycloak.migration.migrators.MigrateTo2_2_0;
@@ -110,7 +111,8 @@ public class LegacyMigrationManager implements MigrationManager {
new MigrateTo18_0_0(),
new MigrateTo20_0_0(),
new MigrateTo21_0_0(),
- new MigrateTo22_0_0()
+ new MigrateTo22_0_0(),
+ new MigrateTo23_0_0()
};
private final KeycloakSession session;
@@ -119,6 +121,7 @@ public class LegacyMigrationManager implements MigrationManager {
this.session = session;
}
+ @Override
public void migrate() {
session.setAttribute(Constants.STORAGE_BATCH_ENABLED, Boolean.getBoolean("keycloak.migration.batch-enabled"));
session.setAttribute(Constants.STORAGE_BATCH_SIZE, Integer.getInteger("keycloak.migration.batch-size"));
@@ -161,6 +164,7 @@ public class LegacyMigrationManager implements MigrationManager {
PATTERN_MATCHER.put(Pattern.compile("^7\\.4\\.\\d+\\.GA$"), RHSSO_VERSION_7_4_KEYCLOAK_VERSION);
}
+ @Override
public void migrate(RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
ModelVersion stored = null;
if (rep.getKeycloakVersion() != null) {
diff --git a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java
index 281e782438d..211c6d8972a 100644
--- a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java
+++ b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java
@@ -38,7 +38,6 @@ import java.util.stream.Collectors;
import org.keycloak.Config;
import org.keycloak.common.Profile;
-import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
@@ -48,6 +47,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.services.messages.Messages;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.userprofile.config.DeclarativeUserProfileModel;
@@ -76,11 +76,10 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
public static final String ID = "declarative-user-profile";
public static final int PROVIDER_PRIORITY = 1;
- public static final String UP_PIECES_COUNT_COMPONENT_CONFIG_KEY = "config-pieces-count";
+ public static final String UP_COMPONENT_CONFIG_KEY = "kc.user.profile.config";
public static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled";
private static final String PARSED_CONFIG_COMPONENT_KEY = "kc.user.profile.metadata";
- private static final String UP_PIECE_COMPONENT_CONFIG_KEY_BASE = "config-piece-";
-
+
private static boolean isDeclarativeConfigurationEnabled;
/**
@@ -254,24 +253,18 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
return;
}
- // store new parts
- List parts = UPConfigUtils.getChunks(configuration, 3800);
- MultivaluedHashMap config = component.getConfig();
-
- config.putSingle(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY, "" + parts.size());
-
- int i = 0;
-
- for (String part : parts) {
- config.putSingle(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + (i++), part);
- }
+ component.getConfig().putSingle(UP_COMPONENT_CONFIG_KEY, configuration);
realm.updateComponent(component);
}
@Override
public List getConfigProperties() {
- return Collections.emptyList();
+ return ProviderConfigurationBuilder.create()
+ .property().name(UP_COMPONENT_CONFIG_KEY)
+ .type(ProviderConfigProperty.STRING_TYPE)
+ .add()
+ .build();
}
@Override
@@ -503,34 +496,14 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
if (model == null)
return null;
- int count = model.get(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY, 0);
- if (count < 1) {
- return defaultRawConfig;
- }
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < count; i++) {
- String v = model.get(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + i);
- if (v != null)
- sb.append(v);
- }
-
- return sb.toString();
+ return model.get(UP_COMPONENT_CONFIG_KEY);
}
private void removeConfigJsonFromComponentModel(ComponentModel model) {
if (model == null)
return;
- int count = model.get(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY, 0);
- if (count < 1) {
- return;
- }
-
- for (int i = 0; i < count; i++) {
- model.getConfig().remove(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + i);
- }
- model.getConfig().remove(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY);
+ model.getConfig().remove(UP_COMPONENT_CONFIG_KEY);
}
@Override
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 73ddc51180b..cf140b3936d 100644
--- a/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java
+++ b/services/src/main/java/org/keycloak/userprofile/config/UPConfigUtils.java
@@ -249,28 +249,6 @@ public class UPConfigUtils {
}
}
- /**
- * Break string to substrings of given length.
- *
- * @param src to break
- * @param partLength
- * @return list of string parts, never null (but can be empty if src is null)
- */
- public static List getChunks(String src, int partLength) {
- List ret = new ArrayList<>();
- if (src != null) {
- int pieces = (src.length() / partLength) + 1;
- for (int i = 0; i < pieces; i++) {
- if ((i + 1) < pieces)
- ret.add(src.substring(i * partLength, (i + 1) * partLength));
- else if (i == 0 || (i * partLength) < src.length())
- ret.add(src.substring(i * partLength));
- }
- }
-
- return ret;
- }
-
/**
* Check if context CAN BE part of the AuthenticationFlow.
*
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
index 23d66ec5ef5..f8b140d71ff 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
@@ -17,9 +17,13 @@
package org.keycloak.testsuite.admin;
-import org.keycloak.admin.client.resource.ComponentResource;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
+import org.keycloak.admin.client.resource.ComponentResource;
import org.keycloak.admin.client.resource.ComponentsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.MultivaluedHashMap;
@@ -32,16 +36,22 @@ import jakarta.ws.rs.core.Response;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.concurrent.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
-import org.apache.commons.lang3.concurrent.BasicThreadFactory;
-import org.hamcrest.Matchers;
+
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
-import static org.junit.Assert.*;
import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author Stian Thorgersen
@@ -323,6 +333,23 @@ public class ComponentsTest extends AbstractAdminTest {
assertThat(returned4.getConfig().get("secret"), contains("${vault.value}"));
}
+ @Test
+ public void testCreateLongValue() {
+ ComponentRepresentation rep = createComponentRepresentation("mycomponent");
+
+ final String randomLongString = RandomStringUtils.random(5000, true, true);
+
+ rep.getConfig().putSingle("required", "Required");
+ rep.getConfig().putSingle("val1", randomLongString);
+
+ String id = createComponent(rep);
+ ComponentRepresentation returned = components.component(id).toRepresentation();
+
+ assertThat(returned.getConfig().size(), equalTo(2));
+ assertNotNull(returned.getConfig().getFirst("val1"));
+ assertThat(returned.getConfig().getFirst("val1"), equalTo(randomLongString));
+ }
+
@Test
public void testLongValueInComponentConfigAscii() throws Exception {
ComponentRepresentation rep = createComponentRepresentation("mycomponent");
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 2b62a553321..ffc96a0d908 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
@@ -21,10 +21,12 @@ import org.apache.commons.io.FileUtils;
import org.hamcrest.Matchers;
import org.jboss.arquillian.container.spi.client.container.LifecycleException;
import org.junit.After;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
import org.keycloak.common.Profile.Feature;
+import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.Strategy;
import org.keycloak.exportimport.dir.DirExportProvider;
@@ -42,8 +44,10 @@ import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.client.resources.TestingExportImportResource;
+import org.keycloak.testsuite.forms.VerifyProfileTest;
import org.keycloak.testsuite.runonserver.RunHelpers;
import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.userprofile.DeclarativeUserProfileProvider;
import java.io.File;
import java.io.IOException;
@@ -60,9 +64,10 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
-import org.junit.BeforeClass;
/**
*
@@ -72,6 +77,8 @@ import org.junit.BeforeClass;
*/
public class ExportImportTest extends AbstractKeycloakTest {
+ private static final String TEST_REALM = "test-realm";
+
@BeforeClass
public static void checkNotMapStorage() {
// Disabled temporarily, re-enable once export/import functionality is implemented for map storage
@@ -109,7 +116,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testRealms.add(testRealm1);
RealmRepresentation testRealm2 = loadJson(getClass().getResourceAsStream("/model/testrealm.json"), RealmRepresentation.class);
- testRealm2.setId("test-realm");
+ testRealm2.setId(TEST_REALM);
setLocalizationTexts(testRealm2);
testRealms.add(testRealm2);
}
@@ -137,7 +144,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
Assert.assertTrue(config.isAdminEventsEnabled());
Assert.assertTrue(config.isAdminEventsDetailsEnabled());
Assert.assertEquals((Long) 600L, config.getEventsExpiration());
- Assert.assertNames(new HashSet(config.getEnabledEventTypes()),"REGISTER", "REGISTER_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT_ERROR");
+ Assert.assertNames(new HashSet<>(config.getEnabledEventTypes()),"REGISTER", "REGISTER_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT_ERROR");
}
private UserRepresentation makeUser(String userName) {
@@ -172,7 +179,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testFullExportImport();
- RealmResource testRealmRealm = adminClient.realm("test-realm");
+ RealmResource testRealmRealm = adminClient.realm(TEST_REALM);
ExportImportUtil.assertDataImportedInRealm(adminClient, testingClient, testRealmRealm.toRepresentation());
// There should be 6 files in target directory (3 realm, 3 user)
@@ -191,7 +198,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testRealmExportImport();
- RealmResource testRealmRealm = adminClient.realm("test-realm");
+ RealmResource testRealmRealm = adminClient.realm(TEST_REALM);
ExportImportUtil.assertDataImportedInRealm(adminClient, testingClient, testRealmRealm.toRepresentation());
// There should be 4 files in target directory (1 realm, 12 users, 5 users per file)
@@ -221,7 +228,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
@Test
public void testSingleFileRealmWithoutBuiltinsImport() throws Throwable {
// Remove test realm
- removeRealm("test-realm");
+ removeRealm(TEST_REALM);
// Set the realm, which doesn't have builtin clients/roles inside JSON
testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
@@ -233,7 +240,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testingClient.testing().exportImport().runImport();
- RealmResource testRealmRealm = adminClient.realm("test-realm");
+ RealmResource testRealmRealm = adminClient.realm(TEST_REALM);
ExportImportUtil.assertDataImportedInRealm(adminClient, testingClient, testRealmRealm.toRepresentation());
}
@@ -262,6 +269,44 @@ public class ExportImportTest extends AbstractKeycloakTest {
Assert.assertTrue("Imported realm hasn't been found!", isRealmPresent("cez"));
}
+ @Test
+ public void testExportUserProfileConfig() {
+ //Enable user profile on realm
+ RealmResource realmRes = adminClient.realm(TEST_REALM);
+ RealmRepresentation realmRep = realmRes.toRepresentation();
+ Map realmAttr = realmRep.getAttributesOrEmpty();
+ realmAttr.put(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
+ realmRep.setAttributes(realmAttr);
+ realmRes.update(realmRep);
+
+ //add some non-default config
+ VerifyProfileTest.setUserProfileConfiguration(realmRes, VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT);
+
+ //export
+ TestingExportImportResource exportImport = testingClient.testing().exportImport();
+ exportImport.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+ exportImport.setAction(ExportImportConfig.ACTION_EXPORT);
+ exportImport.setRealmName(TEST_REALM);
+ String targetFilePath = exportImport.getExportImportTestDirectory() + File.separator + "singleFile-userProfile.json";
+ exportImport.setFile(targetFilePath);
+ exportImport.runExport();
+
+ //remove realm
+ removeRealm(TEST_REALM);
+
+ //import
+ exportImport.setAction(ExportImportConfig.ACTION_IMPORT);
+ exportImport.runImport();
+
+ List userProfileComponents = realmRes.components().query(TEST_REALM, "org.keycloak.userprofile.UserProfileProvider");
+ assertThat(userProfileComponents, notNullValue());
+ assertThat(userProfileComponents, hasSize(1));
+ MultivaluedHashMap config = userProfileComponents.get(0).getConfig();
+ assertThat(config, notNullValue());
+ assertThat(config.size(), equalTo(1));
+ assertThat(config.getFirst(DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY), equalTo(VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT));
+ }
+
@Test
public void testImportIgnoreExistingMissingClientId() {
TestingExportImportResource resource = testingClient.testing().exportImport();
@@ -330,7 +375,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testingClient.testing().exportImport().runExport();
removeRealm("test");
- removeRealm("test-realm");
+ removeRealm(TEST_REALM);
Assert.assertNames(adminClient.realms().findAll(), "master");
Map requiredActionsBeforeImport = new HashMap<>();
@@ -353,7 +398,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testingClient.testing().exportImport().runImport();
// Ensure data are imported back
- Assert.assertNames(adminClient.realms().findAll(), "master", "test", "test-realm");
+ Assert.assertNames(adminClient.realms().findAll(), "master", "test", TEST_REALM);
assertAuthenticated("test", "test-user@localhost", "password");
assertAuthenticated("test", "user1", "password");
@@ -402,7 +447,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
// Delete some realm (and some data in admin realm)
adminClient.realm("test").remove();
- Assert.assertNames(adminClient.realms().findAll(), "test-realm", "master");
+ Assert.assertNames(adminClient.realms().findAll(), TEST_REALM, "master");
assertNotAuthenticated("test", "test-user@localhost", "password");
assertNotAuthenticated("test", "user1", "password");
@@ -417,7 +462,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
testingClient.testing().exportImport().runImport();
// Ensure data are imported back, but just for "test" realm
- Assert.assertNames(adminClient.realms().findAll(), "master", "test", "test-realm");
+ Assert.assertNames(adminClient.realms().findAll(), "master", "test", TEST_REALM);
assertAuthenticated("test", "test-user@localhost", "password");
assertAuthenticated("test", "user1", "password");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
index 7a7c4d9ad9f..a3cebfc4fb0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
@@ -53,6 +53,7 @@ import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentatio
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
+import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -86,15 +87,16 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import static net.bytebuddy.matcher.ElementMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -108,6 +110,7 @@ import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
import static org.keycloak.testsuite.Assert.assertNames;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY;
/**
* @author Bill Burke
@@ -179,6 +182,17 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
.anyMatch(authFlow -> authFlow.getAlias().equalsIgnoreCase("http challenge")));
}
+ protected void testUserProfile(RealmResource realm) {
+ // check user profile config
+ List userProfileComponents = realm.components().query(null, "org.keycloak.userprofile.UserProfileProvider");
+ assertThat(userProfileComponents, hasSize(1));
+
+ ComponentRepresentation component = userProfileComponents.get(0);
+ assertThat(component.getProviderId(), equalTo("declarative-user-profile"));
+ assertThat(component.getConfig().size(), equalTo(1));
+ assertThat(component.getConfig().getList(UP_COMPONENT_CONFIG_KEY), not(empty()));
+ }
+
/**
* @see org.keycloak.migration.migrators.MigrateTo2_0_0
*/
@@ -363,6 +377,13 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testHttpChallengeFlow(migrationRealm);
}
+ /**
+ * @param testUserProfileMigration whether a migrated realm contains a user profile component or not.
+ */
+ protected void testMigrationTo23_0_0(boolean testUserProfileMigration) {
+ if (testUserProfileMigration) testUserProfile(migrationRealm2);
+ }
+
protected void testDeleteAccount(RealmResource realm) {
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
ClientResource accountResource = realm.clients().get(accountClient.getId());
@@ -1043,6 +1064,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testMigrationTo22_0_0();
}
+ protected void testMigrationTo23_x(boolean testUserProfileMigration) {
+ testMigrationTo23_0_0(testUserProfileMigration);
+ }
+
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
if (supportedAuthzServices) {
testDecisionStrategySetOnResourceServer();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java
index 9eec5da4dd8..9c9218204ba 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java
@@ -27,7 +27,7 @@ import java.util.List;
import java.util.Map;
/**
- * Tests that we can import json file from previous version. MigrationTest only tests DB.
+ * Tests that we can import json file from previous version. MigrationTest only tests DB.
*/
public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigrationTest {
@@ -51,6 +51,7 @@ public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigra
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(true);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
index 5d5ba4dfbca..2b51481821a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
@@ -77,6 +77,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(false);
}
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
index ec7fe64f6ac..34626586fa1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
@@ -71,6 +71,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(false);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java
index ec84745d23c..cd94f1a148f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java
@@ -66,6 +66,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(false);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java
index 4af687b20c9..b060a65f1a9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java
@@ -60,6 +60,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(false);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java
index 19854cb7e5f..d204e9aafc3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java
@@ -53,6 +53,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(false);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
index 5aa33859af1..060fc068fcc 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
@@ -68,5 +68,6 @@ public class MigrationTest extends AbstractMigrationTest {
testMigrationTo20_x();
testMigrationTo21_x();
testMigrationTo22_x();
+ testMigrationTo23_x(true);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/CustomUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/CustomUserProfileTest.java
index 0fdcd176449..15ca9bc4a9c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/CustomUserProfileTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/CustomUserProfileTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import org.junit.Assert;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
@@ -31,13 +30,11 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
import org.keycloak.testsuite.arquillian.annotation.SetDefaultProvider;
import org.keycloak.testsuite.runonserver.RunOnServer;
-import org.keycloak.userprofile.DeclarativeUserProfileProvider;
import org.keycloak.userprofile.UserProfile;
import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.userprofile.UserProfileProvider;
import org.keycloak.userprofile.config.UPConfigUtils;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@@ -79,27 +76,6 @@ public class CustomUserProfileTest extends AbstractUserProfileTest {
}
}
- @Test
- public void testConfigurationChunks() {
- getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) CustomUserProfileTest::testConfigurationChunks);
- }
-
- private static void testConfigurationChunks(KeycloakSession session) throws IOException {
- UserProfileProvider provider = getUserProfileProvider(session);
- String newConfig = generateLargeProfileConfig();
-
- provider.setConfiguration(newConfig);
-
- Optional component = getComponentModel(session);
- assertTrue(component.isPresent());
-
- // assert config is persisted in 2 pieces
- Assert.assertEquals("2", component.get().get(DeclarativeUserProfileProvider.UP_PIECES_COUNT_COMPONENT_CONFIG_KEY));
- // assert config is returned correctly
- Assert.assertEquals(newConfig, provider.getConfiguration());
- }
-
-
@Test
public void testDefaultConfig() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) CustomUserProfileTest::testDefaultConfig);
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 7509a44f707..c8f47449250 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
@@ -57,7 +57,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.messages.Messages;
import org.keycloak.testsuite.runonserver.RunOnServer;
import org.keycloak.userprofile.AttributeGroupMetadata;
-import org.keycloak.userprofile.DeclarativeUserProfileProvider;
import org.keycloak.userprofile.config.UPAttribute;
import org.keycloak.userprofile.config.UPAttributePermissions;
import org.keycloak.userprofile.config.UPAttributeRequired;
@@ -752,27 +751,6 @@ public class UserProfileTest extends AbstractUserProfileTest {
}
- @Test
- public void testConfigurationChunks() {
- getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testConfigurationChunks);
- }
-
- private static void testConfigurationChunks(KeycloakSession session) throws IOException {
- ComponentModel component = setAndGetDefaultConfiguration(session).orElse(null);
- assertNotNull(component);
-
- String newConfig = generateLargeProfileConfig();
- UserProfileProvider provider = getUserProfileProvider(session);
-
- provider.setConfiguration(newConfig);
- component = getComponentModel(session).orElse(null);
-
- // assert config is persisted in 2 pieces
- Assert.assertEquals("2", component.get(DeclarativeUserProfileProvider.UP_PIECES_COUNT_COMPONENT_CONFIG_KEY));
- // assert config is returned correctly
- Assert.assertEquals(newConfig, provider.getConfiguration());
- }
-
@Test
public void testResetConfiguration() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testResetConfiguration);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigUtilsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigUtilsTest.java
index f6598eff109..3ee77e9b92a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigUtilsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/profile/config/UPConfigUtilsTest.java
@@ -20,7 +20,6 @@ import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import org.junit.Assert;
@@ -72,35 +71,6 @@ public class UPConfigUtilsTest {
Assert.assertTrue(UPConfigUtils.isRoleForContext(UserProfileContext.REGISTRATION, roles));
}
- @Test
- public void breakString() {
- List ret = UPConfigUtils.getChunks(null, 2);
- Assert.assertEquals(0, ret.size());
-
- ret = UPConfigUtils.getChunks("", 2);
- assertListContent(ret, "");
-
- ret = UPConfigUtils.getChunks("1234567", 3);
- assertListContent(ret, "123", "456", "7");
-
- ret = UPConfigUtils.getChunks("12345678", 3);
- assertListContent(ret, "123", "456", "78");
-
- ret = UPConfigUtils.getChunks("123456789", 3);
- assertListContent(ret, "123", "456", "789");
- }
-
- /**
- * Assert list exactly contains all expected parts in given order
- */
- private void assertListContent(List actual, String... expectedParts) {
- int i = 0;
- Assert.assertEquals(expectedParts.length, actual.size());
- for (String ep : expectedParts) {
- Assert.assertEquals(ep, actual.get(i++));
- }
- }
-
@Test
public void capitalizeFirstLetter() {
Assert.assertNull(UPConfigUtils.capitalizeFirstLetter(null));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-19.0.3.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-19.0.3.json
index 8542f3c15a5..9ec90da5143 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-19.0.3.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-19.0.3.json
@@ -1878,7 +1878,7 @@
"clientOfflineSessionIdleTimeout" : "0",
"cibaInterval" : "5"
},
- "keycloakVersion" : "17.0.0",
+ "keycloakVersion" : "19.0.3",
"userManagedAccessAllowed" : false,
"clientProfiles" : {
"profiles" : [ ]
@@ -2912,6 +2912,17 @@
"identityProviders" : [ ],
"identityProviderMappers" : [ ],
"components" : {
+ "org.keycloak.userprofile.UserProfileProvider": [
+ {
+ "id": "88cef18c-bcd8-40d2-9e7d-d257298317f2",
+ "providerId": "declarative-user-profile",
+ "subComponents": {},
+ "config": {
+ "config-piece-0" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}}},{\"name\":\"email\",\"displayName\":\"${email}\",\"permissions\":{\"edit\":[\"admin\",\"user\"],\"view\":[\"admin\",\"user\"]},\"validations\":{\"email\":{},\"length\":{\"max\":255},\"pattern\":{\"pattern\":\"[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~.-]+@example.nl\",\"error-message\":\"Invalid domain selected\"}},\"annotations\":{\"\":\"\"},\"required\":{\"roles\":[\"user\"]},\"group\":null},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}}},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}}}]}" ],
+ "config-pieces-count" : [ "1" ]
+ }
+ }
+ ],
"org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ {
"id" : "55d8aaa7-2307-4e3f-9b49-4a5cf7f0980c",
"name" : "Allowed Protocol Mapper Types",
@@ -3599,6 +3610,7 @@
"clientAuthenticationFlow" : "clients",
"dockerAuthenticationFlow" : "docker auth",
"attributes" : {
+ "userProfileEnabled" : "true",
"cibaBackchannelTokenDeliveryMode" : "poll",
"cibaExpiresIn" : "120",
"cibaAuthRequestedUserHint" : "login_hint",
@@ -3611,7 +3623,7 @@
"clientOfflineSessionIdleTimeout" : "0",
"cibaInterval" : "5"
},
- "keycloakVersion" : "17.0.0",
+ "keycloakVersion" : "19.0.3",
"userManagedAccessAllowed" : false,
"clientProfiles" : {
"profiles" : [ ]
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/import-userprofile.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/import-userprofile.json
index a529b217c3c..9f63eabc9f3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/import-userprofile.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/import-userprofile.json
@@ -88,8 +88,7 @@
"providerId" : "declarative-user-profile",
"subComponents" : { },
"config" : {
- "config-pieces-count" : [ "1" ],
- "config-piece-0" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{}}},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}}},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]},\"required\":{}},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]}},{\"selector\":{\"scopes\":[\"microprofile-jwt\"]},\"permissions\":{\"view\":[],\"edit\":[]},\"name\":\"test\"}]}" ]
+ "kc.user.profile.config" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{}}},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}}},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]},\"required\":{}},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]}},{\"selector\":{\"scopes\":[\"microprofile-jwt\"]},\"permissions\":{\"view\":[],\"edit\":[]},\"name\":\"test\"}]}" ]
}
} ]
},