diff --git a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java index 642a285f16..f9b8a2081e 100644 --- a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java +++ b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java @@ -44,6 +44,8 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; import org.keycloak.services.messages.Messages; import org.keycloak.userprofile.validator.BlankAttributeValidator; import org.keycloak.userprofile.validator.BrokeringFederatedUsernameHasValueValidator; @@ -55,7 +57,6 @@ import org.keycloak.userprofile.validator.RegistrationEmailAsUsernameEmailValueV import org.keycloak.userprofile.validator.RegistrationEmailAsUsernameUsernameValueValidator; import org.keycloak.userprofile.validator.RegistrationUsernameExistsValidator; import org.keycloak.userprofile.validator.UsernameHasValueValidator; -import org.keycloak.userprofile.validator.UsernameIDNHomographValidator; import org.keycloak.userprofile.validator.UsernameMutationValidator; import org.keycloak.validate.ValidatorConfig; import org.keycloak.validate.validators.EmailValidator; @@ -67,6 +68,9 @@ import org.keycloak.validate.validators.EmailValidator; */ public abstract class AbstractUserProfileProvider implements UserProfileProvider, UserProfileProviderFactory { + public static final String CONFIG_ADMIN_READ_ONLY_ATTRIBUTES = "admin-read-only-attributes"; + public static final String CONFIG_READ_ONLY_ATTRIBUTES = "read-only-attributes"; + private static boolean editUsernameCondition(AttributeContext c) { KeycloakSession session = c.getSession(); KeycloakContext context = session.getContext(); @@ -148,10 +152,10 @@ public abstract class AbstractUserProfileProvider * There are the declarations for creating the built-in validations for read-only attributes. Regardless of the context where * user profiles are used. They are related to internal attributes with hard conditions on them in terms of management. */ - private static String[] DEFAULT_READ_ONLY_ATTRIBUTES = { "KERBEROS_PRINCIPAL", "LDAP_ID", "LDAP_ENTRY_DN", "CREATED_TIMESTAMP", "createTimestamp", "modifyTimestamp", "userCertificate", "saml.persistent.name.id.for.*", "ENABLED", "EMAIL_VERIFIED", "disabledReason" }; - private static String[] DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES = { "KERBEROS_PRINCIPAL", "LDAP_ID", "LDAP_ENTRY_DN", "CREATED_TIMESTAMP", "createTimestamp", "modifyTimestamp" }; - private static Pattern readOnlyAttributesPattern = getRegexPatternString(DEFAULT_READ_ONLY_ATTRIBUTES); - private static Pattern adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES); + private static final String[] DEFAULT_READ_ONLY_ATTRIBUTES = { "KERBEROS_PRINCIPAL", "LDAP_ID", "LDAP_ENTRY_DN", "CREATED_TIMESTAMP", "createTimestamp", "modifyTimestamp", "userCertificate", "saml.persistent.name.id.for.*", "ENABLED", "EMAIL_VERIFIED", "disabledReason" }; + private static final String[] DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES = { "KERBEROS_PRINCIPAL", "LDAP_ID", "LDAP_ENTRY_DN", "CREATED_TIMESTAMP", "createTimestamp", "modifyTimestamp" }; + private static final Pattern readOnlyAttributesPattern = getRegexPatternString(DEFAULT_READ_ONLY_ATTRIBUTES); + private static final Pattern adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES); protected final Map contextualMetadataRegistry; protected final KeycloakSession session; @@ -190,7 +194,7 @@ public abstract class AbstractUserProfileProvider public void init(Config.Scope config) { // make sure registry is clear in case of re-deploy contextualMetadataRegistry.clear(); - Pattern pattern = getRegexPatternString(config.getArray("read-only-attributes")); + Pattern pattern = getRegexPatternString(config.getArray(CONFIG_READ_ONLY_ATTRIBUTES)); AttributeValidatorMetadata readOnlyValidator = null; if (pattern != null) { @@ -377,7 +381,7 @@ public abstract class AbstractUserProfileProvider } private UserProfileMetadata createUserResourceValidation(Config.Scope config) { - Pattern p = getRegexPatternString(config.getArray("admin-read-only-attributes")); + Pattern p = getRegexPatternString(config.getArray(CONFIG_ADMIN_READ_ONLY_ATTRIBUTES)); UserProfileMetadata metadata = new UserProfileMetadata(USER_API); @@ -396,4 +400,22 @@ public abstract class AbstractUserProfileProvider return metadata; } + + @Override + public List getConfigMetadata() { + return ProviderConfigurationBuilder.create() + .property() + .name(CONFIG_READ_ONLY_ATTRIBUTES) + .type(ProviderConfigProperty.MULTIVALUED_STRING_TYPE) + .helpText("Array of regular expressions to identify fields that should be treated read-only so users can't change them.") + .add() + + .property() + .name(CONFIG_ADMIN_READ_ONLY_ATTRIBUTES) + .type(ProviderConfigProperty.MULTIVALUED_STRING_TYPE) + .helpText("Array of regular expressions to identify fields that should be treated read-only so administrators can't change them.") + .add() + + .build(); + } }