Do not allow setting default values for root attributes

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2025-08-06 08:45:22 -03:00
parent 10c510206f
commit ac632d609e
3 changed files with 56 additions and 15 deletions

View File

@ -100,6 +100,8 @@ export const AttributeGeneralSettings = () => {
</SelectOption>
));
const ROOT_ATTRIBUTE = [...USERNAME_EMAIL, "firstName", "lastName"];
return (
<FormProvider {...form}>
<FormAccess role="manage-realm" isHorizontal>
@ -139,11 +141,13 @@ export const AttributeGeneralSettings = () => {
label={t("multivalued")}
labelIcon={t("multivaluedHelp")}
/>
<TextControl
name="defaultValue"
label={t("defaultValue")}
labelIcon={t("defaultValueHelp")}
/>
{!ROOT_ATTRIBUTE.includes(attributeName) && (
<TextControl
name="defaultValue"
label={t("defaultValue")}
labelIcon={t("defaultValueHelp")}
/>
)}
<SelectControl
name="group"
label={t("attributeGroup")}

View File

@ -17,6 +17,7 @@
package org.keycloak.userprofile.config;
import static org.keycloak.common.util.ObjectUtil.isBlank;
import static org.keycloak.userprofile.UserProfileUtil.isRootAttribute;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
@ -149,7 +150,7 @@ public class UPConfigUtils {
}
List<String> errors = new ArrayList<>();
List<String> attributeNames = attributes.stream().map(UPAttribute::getName).collect(Collectors.toList());
List<String> attributeNames = attributes.stream().map(UPAttribute::getName).toList();
for (String name : Arrays.asList(UserModel.USERNAME, UserModel.EMAIL)) {
if (!attributeNames.contains(name)) {
@ -185,15 +186,7 @@ public class UPConfigUtils {
}
if (attributeConfig.getValidations() != null) {
attributeConfig.getValidations().forEach((validator, validatorConfig) -> validateValidationConfig(session, validator, validatorConfig, attributeName, errors));
if (attributeConfig.getDefaultValue() != null) {
attributeConfig.getValidations().forEach((validator, validatorConfig) -> {
ValidationContext context = Validators.validator(session, validator).validate(attributeConfig.getDefaultValue(), attributeName, ValidatorConfig.configFromMap(validatorConfig));
if (!context.isValid()) {
errors.add("Default value is invalid");
}
});
}
validateDefaultValue(session, attributeConfig, errors);
}
if (attributeConfig.getPermissions() != null) {
if (attributeConfig.getPermissions().getView() != null) {
@ -222,6 +215,27 @@ public class UPConfigUtils {
}
}
private static void validateDefaultValue(KeycloakSession session, UPAttribute attributeConfig, List<String> errors) {
String defaultValue = attributeConfig.getDefaultValue();
if (defaultValue == null) {
return;
}
String attributeName = attributeConfig.getName();
if (isRootAttribute(attributeName)) {
errors.add("Default value not supported for attribute '" + attributeName + "'");
} else {
attributeConfig.getValidations().forEach((validator, validatorConfig) -> {
ValidationContext context = Validators.validator(session, validator).validate(defaultValue, attributeName, ValidatorConfig.configFromMap(validatorConfig));
if (!context.isValid()) {
errors.add("Default value for attribute '" + attributeName + "' is invalid");
}
});
}
}
private static void validateAnnotations(Map<String, Object> annotations, List<String> errors, String attributeName) {
if (annotations.containsKey("inputOptions") && !(annotations.get("inputOptions") instanceof List)) {
errors.add(new StringBuilder("Annotation 'inputOptions' configured for attribute '").append(attributeName).append("' must be an array of values!'").toString());

View File

@ -81,6 +81,7 @@ import org.keycloak.userprofile.UserProfile;
import org.keycloak.userprofile.UserProfileConstants;
import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.userprofile.UserProfileProvider;
import org.keycloak.userprofile.UserProfileUtil;
import org.keycloak.userprofile.ValidationException;
import org.keycloak.userprofile.config.UPConfigUtils;
import org.keycloak.userprofile.validator.MultiValueValidator;
@ -2400,6 +2401,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
public void testDefaultValue() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testInvalidConfigDefaultValue);
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testDefaultValue);
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testNoDefaultValueForRootAttributes);
}
private static void testInvalidConfigDefaultValue(KeycloakSession session) {
@ -2438,6 +2440,27 @@ public class UserProfileTest extends AbstractUserProfileTest {
assertThat(actualValue, Matchers.equalTo(expectedValue));
}
private static void testNoDefaultValueForRootAttributes(KeycloakSession session) {
UserProfileProvider provider = getUserProfileProvider(session);
UPConfig upConfig = UPConfigUtils.parseSystemDefaultConfig();
upConfig.getAttribute(UserModel.USERNAME).setDefaultValue("def");
upConfig.getAttribute(UserModel.EMAIL).setDefaultValue("def");
upConfig.getAttribute(UserModel.FIRST_NAME).setDefaultValue("def");
upConfig.getAttribute(UserModel.LAST_NAME).setDefaultValue("def");
try {
provider.setConfiguration(upConfig);
fail("Should fail validation for default value");
} catch (ComponentValidationException cve) {
String message = cve.getMessage();
for (String attributeName : List.of(UserModel.USERNAME, UserModel.EMAIL, UserModel.FIRST_NAME, UserModel.LAST_NAME)) {
if (UserProfileUtil.isRootAttribute(attributeName)) {
assertThat(message, Matchers.containsString("Default value not supported for attribute '" + attributeName + "'"));
}
}
}
}
private static void testMultivalued(KeycloakSession session) {
UserProfileProvider provider = getUserProfileProvider(session);
UPConfig upConfig = UPConfigUtils.parseSystemDefaultConfig();