CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-keycloak--keycloak-common

Common library and dependencies shared with server and all adapters for the Keycloak identity and access management system

Pending
Overview
Eval results
Files

profile-management.mddocs/

Profile Management

This document covers the profile management functionality in the org.keycloak.common.profile package that provides advanced feature flag management, configuration resolvers, and profile customization capabilities.

Profile Configuration Resolver Interface

The ProfileConfigResolver interface defines the contract for resolving profile configurations from various sources.

public interface ProfileConfigResolver {
    /**
     * Gets the profile name to use
     */
    Profile.ProfileName getProfileName();
    
    /**
     * Gets the configuration for a specific feature
     */
    FeatureConfig getFeatureConfig(String feature);
}

FeatureConfig Enum

public enum FeatureConfig {
    /**
     * Feature is explicitly enabled
     */
    ENABLED,
    
    /**
     * Feature is explicitly disabled
     */
    DISABLED,
    
    /**
     * Feature configuration not specified, use default
     */
    UNCONFIGURED
}

Properties-Based Configuration Resolver

The PropertiesProfileConfigResolver class resolves profile configuration from Java properties.

public class PropertiesProfileConfigResolver implements ProfileConfigResolver {
    /**
     * Constructor taking properties object
     */
    public PropertiesProfileConfigResolver(Properties properties);
    
    /**
     * Constructor taking property getter function
     */
    public PropertiesProfileConfigResolver(UnaryOperator<String> getter);
    
    /**
     * Gets profile name from properties
     */
    public Profile.ProfileName getProfileName();
    
    /**
     * Gets feature configuration from properties
     */
    public FeatureConfig getFeatureConfig(String feature);
    
    /**
     * Gets property key for a feature
     */
    public static String getPropertyKey(Feature feature);
    
    /**
     * Gets property key for a feature string
     */
    public static String getPropertyKey(String feature);
}

Usage Examples

// Configure with Properties object
Properties props = new Properties();
props.setProperty("kc.profile", "preview");
props.setProperty("kc.features.authorization", "enabled");
props.setProperty("kc.features.scripts", "disabled");

ProfileConfigResolver resolver = new PropertiesProfileConfigResolver(props);

// Configure with system property getter
ProfileConfigResolver systemResolver = new PropertiesProfileConfigResolver(
    System::getProperty
);

// Configure with environment variable getter
ProfileConfigResolver envResolver = new PropertiesProfileConfigResolver(
    key -> System.getenv(key.replace('.', '_').toUpperCase())
);

// Get property keys for features
String authzKey = PropertiesProfileConfigResolver.getPropertyKey(Feature.AUTHORIZATION);
// Result: "kc.features.authorization"

String scriptsKey = PropertiesProfileConfigResolver.getPropertyKey("scripts");
// Result: "kc.features.scripts"

Comma-Separated List Configuration Resolver

The CommaSeparatedListProfileConfigResolver class parses comma-separated feature lists.

public class CommaSeparatedListProfileConfigResolver implements ProfileConfigResolver {
    /**
     * Constructor taking enabled and disabled feature lists
     */
    public CommaSeparatedListProfileConfigResolver(String enabledFeatures, String disabledFeatures);
    
    /**
     * Gets profile name (always returns default)
     */
    public Profile.ProfileName getProfileName();
    
    /**
     * Gets feature configuration from the lists
     */
    public FeatureConfig getFeatureConfig(String feature);
}

Usage Examples

// Configure with feature lists
String enabled = "authorization,scripts,docker";
String disabled = "web-authn,recovery-codes";

ProfileConfigResolver resolver = new CommaSeparatedListProfileConfigResolver(enabled, disabled);

// Check feature configuration
FeatureConfig authzConfig = resolver.getFeatureConfig("authorization");
// Result: FeatureConfig.ENABLED

FeatureConfig webauthnConfig = resolver.getFeatureConfig("web-authn");
// Result: FeatureConfig.DISABLED

FeatureConfig unconfiguredConfig = resolver.getFeatureConfig("token-exchange");
// Result: FeatureConfig.UNCONFIGURED

// Empty lists
ProfileConfigResolver emptyResolver = new CommaSeparatedListProfileConfigResolver(null, null);
FeatureConfig config = emptyResolver.getFeatureConfig("any-feature");
// Result: FeatureConfig.UNCONFIGURED

Profile Exception

The ProfileException class represents runtime exceptions related to profile operations.

public class ProfileException extends RuntimeException {
    /**
     * Constructor with message
     */
    public ProfileException(String message);
    
    /**
     * Constructor with message and cause
     */
    public ProfileException(String message, Throwable cause);
}

Usage Examples

public void validateProfileConfiguration(ProfileConfigResolver resolver) {
    try {
        Profile.ProfileName profileName = resolver.getProfileName();
        if (profileName == null) {
            throw new ProfileException("Profile name cannot be null");
        }
    } catch (Exception e) {
        throw new ProfileException("Failed to validate profile configuration", e);
    }
}

Advanced Profile Configuration Patterns

Multi-Source Configuration Resolver

public class MultiSourceProfileConfigResolver implements ProfileConfigResolver {
    private final List<ProfileConfigResolver> resolvers;
    
    public MultiSourceProfileConfigResolver(ProfileConfigResolver... resolvers) {
        this.resolvers = Arrays.asList(resolvers);
    }
    
    @Override
    public Profile.ProfileName getProfileName() {
        // Use first non-null profile name
        for (ProfileConfigResolver resolver : resolvers) {
            Profile.ProfileName name = resolver.getProfileName();
            if (name != null) {
                return name;
            }
        }
        return null;
    }
    
    @Override
    public FeatureConfig getFeatureConfig(String feature) {
        // Use first explicit configuration (not UNCONFIGURED)
        for (ProfileConfigResolver resolver : resolvers) {
            FeatureConfig config = resolver.getFeatureConfig(feature);
            if (config != FeatureConfig.UNCONFIGURED) {
                return config;
            }
        }
        return FeatureConfig.UNCONFIGURED;
    }
}

Configuration Validation Utilities

public class ProfileConfigValidator {
    
    public static void validateFeatureConfig(String feature, FeatureConfig config, Set<String> validFeatures) {
        if (config != FeatureConfig.UNCONFIGURED && !validFeatures.contains(feature)) {
            throw new ProfileException("Unknown feature: " + feature);
        }
    }
    
    public static void validateProfileName(Profile.ProfileName profileName) {
        if (profileName == null) {
            throw new ProfileException("Profile name cannot be null");
        }
    }
    
    public static void validateDependencies(Map<String, FeatureConfig> featureConfigs) {
        for (Map.Entry<String, FeatureConfig> entry : featureConfigs.entrySet()) {
            if (entry.getValue() == FeatureConfig.ENABLED) {
                validateFeatureDependencies(entry.getKey(), featureConfigs);
            }
        }
    }
    
    private static void validateFeatureDependencies(String feature, Map<String, FeatureConfig> configs) {
        // Get feature dependencies from Profile.Feature enum
        try {
            Profile.Feature f = Profile.Feature.valueOf(feature.toUpperCase().replace('-', '_'));
            Set<Profile.Feature> dependencies = f.getDependencies();
            
            for (Profile.Feature dep : dependencies) {
                String depName = dep.getKey();
                FeatureConfig depConfig = configs.get(depName);
                
                if (depConfig == FeatureConfig.DISABLED) {
                    throw new ProfileException(
                        String.format("Feature %s requires %s to be enabled", feature, depName)
                    );
                }
            }
        } catch (IllegalArgumentException e) {
            // Feature not found in enum, skip validation
        }
    }
}

Environment-Based Configuration

public class EnvironmentProfileConfigResolver implements ProfileConfigResolver {
    private final String profilePrefix;
    private final String featurePrefix;
    
    public EnvironmentProfileConfigResolver(String profilePrefix, String featurePrefix) {
        this.profilePrefix = profilePrefix;
        this.featurePrefix = featurePrefix;
    }
    
    @Override
    public Profile.ProfileName getProfileName() {
        String profileValue = System.getenv(profilePrefix + "PROFILE");
        if (profileValue == null) {
            return null;
        }
        
        try {
            return Profile.ProfileName.valueOf(profileValue.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw new ProfileException("Invalid profile name: " + profileValue);
        }
    }
    
    @Override
    public FeatureConfig getFeatureConfig(String feature) {
        String envKey = featurePrefix + feature.toUpperCase().replace('-', '_');
        String value = System.getenv(envKey);
        
        if (value == null) {
            return FeatureConfig.UNCONFIGURED;
        }
        
        switch (value.toLowerCase()) {
            case "true":
            case "enabled":
            case "on":
                return FeatureConfig.ENABLED;
            case "false":
            case "disabled":
            case "off":
                return FeatureConfig.DISABLED;
            default:
                throw new ProfileException("Invalid feature config value: " + value + " for " + envKey);
        }
    }
}

JSON Configuration Resolver

public class JsonProfileConfigResolver implements ProfileConfigResolver {
    private final JsonObject config;
    
    public JsonProfileConfigResolver(String jsonConfig) {
        try {
            this.config = JsonParser.parseString(jsonConfig).getAsJsonObject();
        } catch (Exception e) {
            throw new ProfileException("Invalid JSON configuration", e);
        }
    }
    
    @Override
    public Profile.ProfileName getProfileName() {
        if (config.has("profile")) {
            String profileName = config.get("profile").getAsString();
            try {
                return Profile.ProfileName.valueOf(profileName.toUpperCase());
            } catch (IllegalArgumentException e) {
                throw new ProfileException("Invalid profile name: " + profileName);
            }
        }
        return null;
    }
    
    @Override
    public FeatureConfig getFeatureConfig(String feature) {
        if (config.has("features")) {
            JsonObject features = config.getAsJsonObject("features");
            if (features.has(feature)) {
                boolean enabled = features.get(feature).getAsBoolean();
                return enabled ? FeatureConfig.ENABLED : FeatureConfig.DISABLED;
            }
        }
        return FeatureConfig.UNCONFIGURED;
    }
}

Complete Profile Configuration Example

public class ProfileManager {
    
    public static Profile configureProfile() {
        // Create multiple configuration sources
        ProfileConfigResolver[] resolvers = {
            // 1. System properties (highest priority)
            new PropertiesProfileConfigResolver(System::getProperty),
            
            // 2. Environment variables
            new EnvironmentProfileConfigResolver("KC_", "KC_FEATURE_"),
            
            // 3. Configuration file
            createFileConfigResolver("keycloak.properties"),
            
            // 4. Default configuration
            new CommaSeparatedListProfileConfigResolver("authorization", null)
        };
        
        // Combine resolvers with priority order
        ProfileConfigResolver resolver = new MultiSourceProfileConfigResolver(resolvers);
        
        // Validate configuration
        validateConfiguration(resolver);
        
        // Configure profile
        return Profile.configure(resolver);
    }
    
    private static ProfileConfigResolver createFileConfigResolver(String filename) {
        try {
            Properties props = new Properties();
            try (InputStream is = ProfileManager.class.getClassLoader().getResourceAsStream(filename)) {
                if (is != null) {
                    props.load(is);
                }
            }
            return new PropertiesProfileConfigResolver(props);
        } catch (Exception e) {
            throw new ProfileException("Failed to load configuration file: " + filename, e);
        }
    }
    
    private static void validateConfiguration(ProfileConfigResolver resolver) {
        try {
            ProfileConfigValidator.validateProfileName(resolver.getProfileName());
            
            // Validate key features
            Set<String> keyFeatures = Set.of("authorization", "scripts", "docker", "web-authn");
            for (String feature : keyFeatures) {
                FeatureConfig config = resolver.getFeatureConfig(feature);
                ProfileConfigValidator.validateFeatureConfig(feature, config, 
                    Profile.getAllUnversionedFeatureNames());
            }
        } catch (Exception e) {
            throw new ProfileException("Configuration validation failed", e);
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-keycloak--keycloak-common

docs

constants-configuration.md

core-functionality.md

crypto-utilities.md

enums-types.md

index.md

profile-management.md

reflection-utilities.md

utility-functions.md

tile.json