diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 6df206abbb..81f74c5360 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -101,6 +101,7 @@ import org.keycloak.quarkus.runtime.storage.database.jpa.NamedJpaConnectionProvi import org.keycloak.quarkus.runtime.themes.FlatClasspathThemeResourceProviderFactory; import org.keycloak.representations.provider.ScriptProviderDescriptor; import org.keycloak.representations.provider.ScriptProviderMetadata; +import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.services.ServicesLogger; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.theme.ClasspathThemeProviderFactory; @@ -112,6 +113,7 @@ import org.keycloak.transaction.JBossJtaTransactionManagerLookup; import org.keycloak.url.DefaultHostnameProviderFactory; import org.keycloak.url.FixedHostnameProviderFactory; import org.keycloak.url.RequestHostnameProviderFactory; +import org.keycloak.userprofile.DeclarativeUserProfileProviderFactory; import org.keycloak.util.JsonSerialization; import org.keycloak.vault.FilesKeystoreVaultProviderFactory; import org.keycloak.vault.FilesPlainTextVaultProviderFactory; @@ -134,7 +136,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Properties; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.jar.JarEntry; @@ -259,6 +260,27 @@ class KeycloakProcessor { } } + /** + * Parse the default configuration for the User Profile provider + */ + @BuildStep + @Produce(UserProfileBuildItem.class) + UserProfileBuildItem parseDefaultUserProfileConfig() { + final UPConfig defaultConfig = DeclarativeUserProfileProviderFactory.parseDefaultConfig(); + logger.debug("Parsing default configuration for the User Profile provider"); + return new UserProfileBuildItem(defaultConfig); + } + + /** + * Set the default configuration to the User Profile provider + */ + @BuildStep + @Consume(ProfileBuildItem.class) + @Record(ExecutionTime.STATIC_INIT) + void setDefaultUserProfileConfig(KeycloakRecorder recorder, UserProfileBuildItem configuration) { + recorder.setDefaultUserProfileConfiguration(configuration.getDefaultConfig()); + } + /** *

Configures the persistence unit for Quarkus. * diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/UserProfileBuildItem.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/UserProfileBuildItem.java new file mode 100644 index 0000000000..d88be338fc --- /dev/null +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/UserProfileBuildItem.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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.quarkus.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; +import org.keycloak.representations.userprofile.config.UPConfig; + +/** + * Build item that store default configuration for a User Profile provider + */ +public final class UserProfileBuildItem extends SimpleBuildItem { + private final UPConfig defaultConfig; + + public UserProfileBuildItem(UPConfig defaultConfig) { + this.defaultConfig = defaultConfig; + } + + public UPConfig getDefaultConfig() { + return defaultConfig; + } +} \ No newline at end of file diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java index e28dce4e72..d5ffa75a88 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java @@ -49,6 +49,7 @@ import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.Spi; import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory; +import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.theme.ClasspathThemeProviderFactory; import org.keycloak.truststore.TruststoreBuilder; @@ -56,6 +57,7 @@ import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import liquibase.servicelocator.ServiceLocator; +import org.keycloak.userprofile.DeclarativeUserProfileProviderFactory; @Recorder public class KeycloakRecorder { @@ -123,6 +125,10 @@ public class KeycloakRecorder { } } + public void setDefaultUserProfileConfiguration(UPConfig configuration) { + DeclarativeUserProfileProviderFactory.setDefaultConfig(configuration); + } + public void registerShutdownHook(ShutdownContext shutdownContext) { shutdownContext.addShutdownTask(new Runnable() { @Override diff --git a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java index 4ac172983e..c287adbb1d 100644 --- a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java +++ b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java @@ -87,9 +87,12 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide private static final Pattern readOnlyAttributesPattern = getRegexPatternString(DEFAULT_READ_ONLY_ATTRIBUTES); private static final Pattern adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES); - private UPConfig parsedDefaultRawConfig; + private static volatile UPConfig PARSED_DEFAULT_RAW_CONFIG; private final Map contextualMetadataRegistry = new HashMap<>(); + public static void setDefaultConfig(UPConfig defaultConfig) { + PARSED_DEFAULT_RAW_CONFIG = defaultConfig; + } private static boolean editUsernameCondition(AttributeContext c) { KeycloakSession session = c.getSession(); @@ -203,9 +206,15 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide return null; } + public static UPConfig parseDefaultConfig() { + return UPConfigUtils.parseDefaultConfig(); + } + @Override public void init(Config.Scope config) { - parsedDefaultRawConfig = UPConfigUtils.parseDefaultConfig(); + if (PARSED_DEFAULT_RAW_CONFIG == null) { + setDefaultConfig(parseDefaultConfig()); + } // make sure registry is clear in case of re-deploy contextualMetadataRegistry.clear(); @@ -320,7 +329,7 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide */ protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata) { // default metadata for each context is based on the default realm configuration - return new DeclarativeUserProfileProvider(null, this).decorateUserProfileForCache(metadata, parsedDefaultRawConfig); + return new DeclarativeUserProfileProvider(null, this).decorateUserProfileForCache(metadata, PARSED_DEFAULT_RAW_CONFIG); } private AttributeValidatorMetadata createReadOnlyAttributeUnchangedValidator(Pattern pattern) { @@ -469,7 +478,7 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide // GETTER METHODS FOR INTERNAL FIELDS protected UPConfig getParsedDefaultRawConfig() { - return parsedDefaultRawConfig; + return PARSED_DEFAULT_RAW_CONFIG; } protected Map getContextualMetadataRegistry() {