Parse default UserProfile configuration in the build time

Closes #24890

Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Martin Bartoš 2024-01-17 21:18:21 +01:00 committed by Pedro Igor
parent e7d842ea32
commit 98be32d9ff
4 changed files with 78 additions and 5 deletions

View file

@ -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());
}
/**
* <p>Configures the persistence unit for Quarkus.
*

View file

@ -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;
}
}

View file

@ -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

View file

@ -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<UserProfileContext, UserProfileMetadata> 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<UserProfileContext, UserProfileMetadata> getContextualMetadataRegistry() {