mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-01 04:30:44 -05:00
committed by
Alexander Schwartz
parent
71777df3d9
commit
a0f04fa2be
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--
|
||||
~ * 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.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
|
||||
<changeSet author="keycloak" id="23.0.0-12062">
|
||||
|
||||
<addColumn tableName="COMPONENT_CONFIG">
|
||||
<column name="VALUE_NEW" type="NCLOB" />
|
||||
</addColumn>
|
||||
|
||||
<update tableName="COMPONENT_CONFIG">
|
||||
<column name="VALUE_NEW" valueComputed="VALUE"/>
|
||||
</update>
|
||||
|
||||
<dropColumn tableName="COMPONENT_CONFIG" columnName="VALUE"/>
|
||||
<renameColumn tableName="COMPONENT_CONFIG" oldColumnName="VALUE_NEW" newColumnName="VALUE" columnDataType="NCLOB"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -78,5 +78,6 @@
|
||||
<include file="META-INF/jpa-changelog-21.0.2.xml"/>
|
||||
<include file="META-INF/jpa-changelog-21.1.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-22.0.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-23.0.0.xml"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
||||
+81
@@ -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<ComponentModel> 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;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -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) {
|
||||
|
||||
+11
-38
@@ -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<String> parts = UPConfigUtils.getChunks(configuration, 3800);
|
||||
MultivaluedHashMap<String, String> 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<ProviderConfigProperty> 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
|
||||
|
||||
@@ -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<String> getChunks(String src, int partLength) {
|
||||
List<String> 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.
|
||||
*
|
||||
|
||||
+34
-7
@@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
@@ -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");
|
||||
|
||||
+56
-11
@@ -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<String, String> 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<ComponentRepresentation> userProfileComponents = realmRes.components().query(TEST_REALM, "org.keycloak.userprofile.UserProfileProvider");
|
||||
assertThat(userProfileComponents, notNullValue());
|
||||
assertThat(userProfileComponents, hasSize(1));
|
||||
MultivaluedHashMap<String, String> 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<String, RequiredActionProviderRepresentation> 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");
|
||||
|
||||
+26
-1
@@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -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<ComponentRepresentation> 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();
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -77,6 +77,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -66,6 +66,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -53,6 +53,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -68,5 +68,6 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||
testMigrationTo20_x();
|
||||
testMigrationTo21_x();
|
||||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(true);
|
||||
}
|
||||
}
|
||||
|
||||
-24
@@ -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<ComponentModel> 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);
|
||||
|
||||
-22
@@ -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);
|
||||
|
||||
-30
@@ -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<String> 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<String> 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));
|
||||
|
||||
+14
-2
@@ -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" : [ ]
|
||||
|
||||
+1
-2
@@ -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\"}]}" ]
|
||||
}
|
||||
} ]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user